Homepage: http://linuxhelp.150m.com/


A Script To Unpackage Archives.


The following script developed over time, with new sections added as I had to deal with new types of archives. The common features were then abstracted to a function unpack_it(). It will unpack .TAR .TGZ .TAR.BZ2, RPM, DEB, CAB and CPIO (including initrd) archives.

It opens all archives in a new directory and leaves the original archive intact. That directory name, is either the one suggested in the archive itself, or the name determined by stipping .tar.bz2 .rpm .tgz etc from the archive name, or by adding .dir to it, if the archive name has none of the common extentions.

You can download it from here (version 2). To install it:

 bunzip2 unpack.bz2
 chmod +x unpack
 mv unpack /usr/bin/

To run the program, enter:

 unpack -v archive1 archive2 archive3 ...

The -v means verbose mode. To unpack all the archives in a directory, use the following:

 unpack *

You might have to tweak the script if it tries to unpack non-archive files.

Unpack, requires awk, file, gzip, bzip2, tar, zip, ar, rpm2cpio and cabextract for full functionality, however, it will still run without some of these, eg, without cabextract it will do everything as usual, except unpack Microsoft cabinet files. Apart from cabextract and rpm2cpio, these programs are included in almost all Linux distributions.

Unpack opens compressed files to see if there is an archive inside, if an archive is not found, the uncompressed file is deleted.

Of course, you can add further functionality to unpack, as you see fit.

In the process of writing this article, I came across 7-Zip, which claims to be able to unpackage RAR, CAB, ISO, ARJ, LZH, CHM, Z, CPIO, RPM, DEB and NSIS archives. You can download Windows binaries from www.7-zip.org. You can download a 32-bit i386 Linux binary, and the source code, from sourceforge.net.

7-Zip does what it claims, but appears to have been designed to be inconvenient to use (so that you will buy a more convenient version?) and really needs a wrapper (something like the following script) to make it user-friendly.


#!/bin/bash

# Usage: unpack $1 $2,.... where $1 $2,... are tar, zip, rpm, deb, cpio, ar or
# cabinet archives. If the archive lists a common directory, then it is opened
# in that directory. Otherwise it will be opened in the directory whose name is
# determined by stipping .tar.bz2 .rpm .tgz etc from the archive name or adding
# .dir to it, if it has no such extentions.

# Requires file, awk, gzip, bzip2, tar, rpm2cpio, zip, cabextract and ar for
# full functionality. Cabextract is from http://www.cabextract.org.uk/

# Opening an archive in a new directory and renaming the directory according
# to what is found, causes problems when one wishes to add the content to an
# existing directory structure. Thus, we first list the content to determine
# the best way to handle the archive.

# Copyright held by linuxhelp.150m.com webmaster (previously linux.coconia.net)

# As long as this header is not removed, this script my be redistributed under
# the terms of the GNU General Public Licence, version 2.

function unpack_it() {
    [[ -n $1 ]] && ADIR=$($1 $B | awk 'BEGIN{n=0;FS="/";x="."}{if(x!=$1){n++};x=$1}END{if(n==1){print x}}')
    if [[ -z $ADIR ]]; then
	$2 $B
    else
        if [[ -d ../$ADIR ]] && [[ "$ADIR" != "$NAME" ]]; then
	    echo -en "\n\033[31mWarning:\033[m\017 Directory \033[32m$ADIR\033[m\017 exists.
Hit \033[34mReturn\033[m\017 to add (overwrite) into this directory.
Hit \033[34mAny Letter\033[m\017 to continue to next archive (or exit).\n"
	    read X
	    if [[ -n $X ]]; then
		rm $A 2>/dev/null; cd ../; rmdir $NAME 2>/dev/null; ADIR=""
		continue
	    fi
        fi
	cd ../; $2 $NAME/$B; cd $ADIR
    fi
}

[[ -z $1 ]] && echo -e "\n\033[31mYou must provide an archive to unpack.\033[m\017\n" && exit

for FILE in "$@"
do
if  [[ $FILE == -* ]]; then
    [[ $FILE == -*v* ]] && v="v"
    [[ $FILE == -*h* ]] && echo -e "\n-h   this help\n-v   verbose\n" && exit
    continue
fi
TYPE=$(file -b $FILE)
[[ $TYPE == cannot* ]] && echo -e "\n\033[31mCannot find file.\033[m\017\n" && continue
[[ $TYPE == *[zZ][iI][pP]* || $TYPE == *archive* || $TYPE == *cpio* || $TYPE == RPM* \
|| $TYPE == Debian* || $TYPE == [NP]E* || $TYPE == *Cabinet* ]] || continue
NAME="$FILE"; A="$FILE"; B="../$FILE"
for item in gz GZ bz2 BZ2 tbz2 TBZ2 tb2 TB2 z Z zip ZIP cpio CPIO rpm RPM deb DEB ar \
AR a A cab CAB exe EXE tar TAR; do NAME=${NAME%.$item}; done

[[ -e "$NAME" ]] && [[ ! -d "$NAME" ]] && NAME="$NAME.dir"

if [[ -d "$NAME" ]]; then
    echo -en "\n\033[31mWarning:\033[m\017 Directory \033[32m$NAME\033[m\017 exists.
Hit \033[34mReturn\033[m\017 to add (overwrite) into this directory.
Hit \033[34mAny Letter\033[m\017 to continue to next archive (or exit).\n"
    read X
    [[ -n $X ]] && continue
fi
mkdir $NAME 2>/dev/null
cd $NAME
if [[ $TYPE == gzip* ]]; then
    gunzip -c $B > $FILE; TYPE=$(file -b $FILE); B=$FILE
fi
if [[ $TYPE == bzip2* ]]; then
    bunzip2 -c $B > $FILE; TYPE=$(file -b $FILE); B=$FILE
fi
if [[ $TYPE == *tar\ ar* ]]; then
    unpack_it "tar -tf" "tar -${v}xf"
fi
if [[ $TYPE == *cpio* ]]; then
    unpack_it "cpio -t --quiet -I" "cpio -${v}uid --quiet -I"
fi
if [[ $TYPE == *\ [zZ][iI][pP]\ * || $TYPE == Zip* ]]; then
    [[ -z $v ]] && U="-qq"
    unpack_it "unzip -Z1" "unzip $U -o"
fi
if [[ $TYPE == RPM* ]]; then
    rpm2cpio $B | cpio -${v}uid --quiet
fi
if [[ $TYPE == Debian* ]]; then
    ar -xf $B
    tar -${v}xf data.tar.gz
    rm data.tar.gz
    mkdir control 2>/dev/null
    tar -C control -${v}xf control.tar.gz
    rm control.tar.gz
fi
if [[ $TYPE == *\ ar\ ar* ]]; then
    ar -${v}xf $B
fi
if [[ $TYPE == [NP]E* && ! $TYPE == *ZIP* ]] || [[ $TYPE == *Cabinet* ]]; then
    [[ -z $v ]] && U="-q"
    ADIR=$(cabextract -l $B | awk 'NR>3&&$6!=""{print $6}' \
| awk 'BEGIN{n=0;FS="/";x="."}{if(x!=$1){n++};x=$1}END{if(n==1){print x}}')
    unpack_it "" "cabextract $U"
fi
cd ../
rm $NAME/$A 2>/dev/null; rmdir $NAME 2>/dev/null
[[ -d $ADIR ]] && echo -e "\nThe archive \033[34m$A\033[m\017 has been \
opened in the directory: \033[31m$ADIR\033[m\017\n" && ADIR="" && continue
[[ -d $NAME ]] && echo -e "\nThe archive \033[34m$A\033[m\017 has been \
opened in the directory: \033[31m$NAME\033[m\017\n" && continue
echo -e "\n\033[31mUnknown archive type.\033[m\017\n"
done


I had a couple of hours to spare, so I wrote the above mentioned wrapper.

Download 7-Zip and the wrapper 7unpack (version 2). Install 7-Zip as follows.

For 32-bit i386 systems there is a binary package:

 tar -xf p7zip_4.53_x86_linux_bin.tar.bz2
 cd p7zip_4.53
 ./install.sh

For all other systems, you need to compile from source:

 tar -xf p7zip_4.53_src_all.tar.bz2
 cd p7zip_4.53
 cp makefile.linux_amd64 makefile.machine  (for 64-bit AMD systems)
 make all3
 make install

By default 7z will be installed to /usr/local/bin/7z.

If you choose elsewhere, you will need to change the variable $PROG="/usr/local/bin/7z"

To install 7unpack:

 bunzip2 7unpack.bz2
 chmod +x 7unpack
 mv 7unpack /usr/bin/

To run the program, enter:

 7unpack archive1 archive2 archive3 ...  or  7unpack *

There are no command line options. This version requires the programs awk, file and 7z (7-Zip) for full functionality. It should unpack all the archives that 7-Zip unpacks (although, in some untested cases, you may need to tweak the script a little for this to happen).