Tuesday, August 19, 2008

How to extract iso file with bash shell script

Hi,
Few days ago, I faced a problem, I needed to extract an iso file but did't have the appropriate permission for "looping" it :
mkdir -p /mnt/disk
mount -o loop disk1.iso /mnt/disk

So , I had to write a shell script which extract the iso file to a destination directory.
(There might be an easiest way to do this, But this was my way)

Please let me introduce you the "isoinfo" command.
I will use only two options of it:
isoinfo -R -l : List all the iso files in hierarchic way , For example:

#> isoinfo -R -l -i RHEL4-U4-i386-ES-disc5.iso
Will reproduce:
....
Directory listing of /RedHat/
drwxrwxr-x 3 0 0 2048 Aug 3 2006 [ 29 02] .
drwxrwxr-x 3 0 0 2048 Aug 3 2006 [ 28 02] ..
drwxrwxr-x 2 0 0 20480 Aug 3 2006 [ 30 02] RPMS
-r--r--r-- 1 0 0 216 Aug 3 2006 [ 65 00] TRANS.TBL

Directory listing of /RedHat/RPMS/
drwxrwxr-x 2 0 0 20480 Aug 3 2006 [ 30 02] .
drwxrwxr-x 3 0 0 2048 Aug 3 2006 [ 29 02] ..
-r--r--r-- 1 0 0 25312 Aug 3 2006 [ 66 00] TRANS.TBL
-rw-r--r-- 63 0 0 2423867 Jul 28 2006 [ 79 00] evolution-devel-2.0.2-27.rhel4.6.i386.rpm
-rw-r--r-- 193 0 0 27768 Jun 7 2005 [ 1263 00] gedit-devel-2.8.1-4.i386.rpm
-rw-r--r-- 196 0 0 24491673 Jan 5 2005 [ 1277 00] gimp-print-cups-4.2.7-2.i386.rpm
-rw-r--r-- 197 0 0 565998 Jan 5 2005 [ 13236 00] gimp-print-devel-4.2.7-2.i386.rpm
-rw-r--r-- 196 0 0 12706 Jan 5 2005 [ 13513 00] gnome-python2-applet-2.6.0-3.i386.rpm
-rw-r--r-- 196 0 0 23924 Jan 5 2005 [ 13520 00] gnome-python2-gconf-2.6.0-3.i386.rpm
.....

And to extract a specific file from the iso:
isoinfo -R -i -x

Now, Lets go to the script:

First, the interpreter :
#!/bin/bash

# Now, Some global defines
ISO_FILE="disk1.iso"
ISO_INFO="isoinfo"
OUT_DIR="./kickstart"
TMP_FILES="./files.tmp"
DIR_PREFIX="Directory listing of "

# Save all the files tree in tmp log file
${ISO_INFO} -R -l -i ${ISO_FILE} > ${TMP_FILES}

# If the output dir already exist, remove it
if [ -e ${OUT_DIR} ]
then
\rm -rf ${OUT_DIR}
fi

# Read the file tree line by line
exec< ${TMP_FILES}
while read node
do
# If line not empty
if [ -n "$node" ]
then
# If line starts with "Directory listing of", Its a directory, see output sample above
if [[ $node == "${DIR_PREFIX}"* ]]
then
# Remove the "Directory listing of " prefix
dir=`echo $node | sed s/"${DIR_PREFIX}"//g`
# And create the directory under the OUT_DIR directory
# mkdir -p = make parent directories as needed (from the man page)
mkdir -p ${OUT_DIR}/$dir
else
# As you can see in the output sample above, both files and directories are listed together,
# the directories can be identified by the first letter in its attribute columns "d", So I will ignore
# them since we handled directories in the previous case
if [[ $node != "d"* ]]
then
# File
file=`echo $node | cut -d" " -f12`
# Actually, I don't need to recheck again the "." and ".." directory, But, Just in case
if [ $file != "." -a $file != ".." ]
then
# Create full file path
filepath=$dir$file
# Create the file
touch ${OUT_DIR}/${filepath}
# Extract the file to its location
${ISO_INFO} -R -i ${ISO_FILE} -x ${filepath} > ${OUT_DIR}/${filepath}
fi
fi
fi
fi
done

# If exist, remove the temporary files list
if [ -f ${TMP_FILES} ]
then
\rm -rf ${TMP_FILES}
fi

Well, There are much more things to add for this script (for example : status checking here and there), But the main functionality is here.

Ofer.

2 comments:

cjwatson said...

bsdtar is definitely your friend. It's much easier than fighting with isoinfo and so on.

Neil McGill said...

Very nice. I wrapped this up in a tool, posted at https://github.com/goblinhack/isoread FYI