simple unpack script

hi all....

I'm a newbie to Shell scripting and am trying to make a simple script that can unpack a variety of .tgz , .zip, .tar or .tar.gz archives to the a current directory

i want the script to create a folder equivalent to the base name of the archive and then move the archive to a subdirectory named archive_store

so far i have done the following:

__________________________________

#!/bin/sh

for I in *.tar.gz;
do
A=`basename $I .tar.gz`
mkdir $A
cd $A
gunzip -c ../$I | tar xf -
cd..
done

_____________________________________

Need some help to expand this script to handle the other formats....

Thank you in advance and any help would be greatly appreciated:b:.

Sounds like a case statement. For each file type, assign the commands to use to a set of variables, and then in the main program, simply do what those variables say. Start by refactoring the script you already have, then add more cases. You could start with an easy one like .tgz

untgz () {
  gunzip -c <"$1" | tar xf -
}

for l in "$@"; do  # invoke this on a bunch of named files
  case $l in *.tar.gz) ext=".tar.gz"; command=untgz;; esac
  :
  A=`basename "$1" "$ext"`
  :
  $command "$1"
  cd ..
done

thanks for your help era... will give it ago and post back...

was able to expand the script using the example as follows... although am unable to make it run... have changed rights of file "chmod 755 filename" and typed "./filename" but there is no result

________________________________
#!/bin/sh

untargz () {
gunzip -c <"$1" | tar xf -
}

untgz () {
gunzip -c <"$1" | tar xf -
}

unzip () {
unzip "$1"
}

for l in "$@";

do

case $l in

*.tar.gz\) ext=".tar.gz"; command=untargz

;;

 *.tgz\) ext=".tgz"; command=untgz
;;

 *.zip\) ext=".zip"; command=unzip

esac

:
A=`basename "$1" "$ext"`
:
$command "$1"
cd ..

done
________________________________________________

If anyone could suggest how to improve this script in any way and what i might be doing wrong when trying to make it run it would be much appreciated

Thanks once again

The : are where you are supposed to use your imagination and/or the code you already wrote. (Sorry for not making that clear.) As it is now, the script simply does basically nothing. Also note the comment which says you invoke it on the files you want to unpack.

No need to have two identical functions, you can use the same function for tar.gz and tgz; and there is no need to write a function for unzip, since you can just say command=unzip and have it use the unzip command you already have on your computer (oh, and take care not to create a recursive function which invokes itself!)

I seem to have mixed up $1 (dollar one) and $l (dollar ell). Inside the function you want the $1 (one) but in the for loop, the file you are currently processing is $l (ell).

okay thanks again era will give it another go... kind of going in circles because there is so much that i dont know.... will try recode in simpler form

Okay so i have got it down to this.... now i am completely lost...

i know that i have to add a few more lines of code on how the files will be handled once uncompressed. ie : mkdir, name=basename. Just unsure of where to start

sorry if im being really draining but i dont know how to structure the last part.....

#!/bin/sh

for file in "$@"
do
case $file in

\*.zip|*.ZIP\)
    unzip "$file"
    ;;

*.tar\)
    tar xvpf "$file"
    ;;

\*.tgz|\*tar.gz|*.tar.Z\)
    gunzip -c  "$file" | tar xvpf \-
    ;;


esac

done

The idea was to put the specific parts in the case statement and then have a general-purpose piece of code just like you had already at the end, except it uses whatever was defined in the case statement above. But okay, here you are.

untgz () {
  gunzip -c <"$1" | tar xf -
}

for l in "$@"; do  # invoke this on a bunch of named files
  case $l in 
    *.tar.gz) ext=".tar.gz"; command=untgz;;
    *.tgz) ext=".tgz"; command=untgz;;
    *.tar.Z) ext=".tar.Z"; command=untgz;;
    *.zip) ext=".zip"; command=unzip;;
    *.ZIP) ext=".ZIP"; command=unzip;;
    *) echo "$0: cannot handle $l, skipping" >&2
        continue ;;
  esac

  A=`basename "$l" "$ext"`
  mkdir "$A"
  cd "$A"
  $command "$1"
  cd ..
done

The function is required for tar.gz because we want a command which takes exactly one parameter as its last argument, so we have to rearrange things a bit and create a function which does that. For most formats you will simply need to add a command= which already works as specified (archive name is sole argument to command), so you don't need a separate function for it (witness unzip).

The extension handling is kind of ugly; with associative arrays that would be easy to put into a table, but the shell doesn't have that facility. You could still think about ways to make it a bit less atrocious. Maybe a while loop which reads extensions and commands?

  # ... code as above, this replaces just the case statement
  ext=
  while read x cmd; do
    case $l in "$x") ext=$x; command=$cmd; break;;
  done <<____HERE
    .zip  unzip
    .ZIP  unzip
    .tar.gz  untgz
    .tar.Z   untgz
    .tgz  untgz
____HERE

  case $ext in '') echo "$0: dunno how to handle $l -- skipping" >&2 ; continue;; esac
  # ... continue as before with A=basename etc

Next you will tell us this was a homework assignment and your lecturer didn't understand how "your" code works, and gave you an F. Oh well.

Actually, your code looks good as well, you just need to put the mkdir etc before the case and the cd .. after the case, but then you will need to figure out a new way to extract just the base name of the archive. It's generally a good idea to put all related information in one place, thus I wanted the case statement to contain all the stuff which is specific to each file type.

hi era.... firstly thanks for your help.....

i know its like you are spoon feeding me but im still unsure how i would handle the basename in a while loop expanding on the first script.

thankyou again

Can't really help you there, I changed your code precisely to solve this problem.

okay please tell me if i am being annoying.... but i think i have lost track of where to go next.... sorry if im not following your hints but this is a massive learning curve for me. I am truely grateful for your help..

correct me if im wrong but, in the bellow code we have a case statement to handle all the different extension types... then a function "untgz" to handle one parameter at a time... in the main program we have an attempt to handle the basename of the file currently bieng read.

im sorry but could you possibly tell me where to go next. I dont want you to tell me the solution just help me understand what part of my code i need to rewrite, comands to research etc....

If i sound lost it mainly because ive only been using unix and doing shell scripting for three weeks.

thankyou once again

#!/bin/sh
untgz () {
  gunzip -c <"$1" | tar xf -
}

for l in "$@"; 
  case $l in 
    *.tar.gz) ext=".tar.gz"; command=untgz;;
    *.tgz) ext=".tgz"; command=untgz;;
    *.tar.Z) ext=".tar.Z"; command=untgz;;
    *.zip) ext=".zip"; command=unzip;;
    *.ZIP) ext=".ZIP"; command=unzip;;
    *) echo "$0: cannot handle $l, skipping" >&2
        continue ;;
  esac

  A=`basename "$l" "$ext"` #is this where i should have my while loop?
  mkdir "$A"
  cd "$A"
  $command "$1"
  cd ..
done

I'm not sure what problems remain to be solved. Doesn't it do what you want already?

well when i run it i have various file in the same folder to test i.e - a.tgz , b.tar.gz, c.zip - nothing happens. I have even typed "echo $shell" to see where shell files are located and changed the start of the script to match.

what would be a better way to debug?

Add prints here and there to see what it's doing. And maybe some error checking too.

A=`basename "$l" "$ext"`
echo file $l, dirname $A, extension $ext
mkdir "$A@ || { echo "$0: Could not mkdir $A -- skipping" >&2; continue; }

You still seem to have "dollar one" in some places where you should have "dollar ell".

thanks for all the help..... have worked out what i have been doing wrong... just got a few more questions.....

currently when i run the script i do so in the following way
i.e ./script foo.tgz # name of file, then file i want to unpack

1) how or what would i have to do to get the script to run without having to state the file i want to unpack?

i.e typing only ---> ./script <----- # the script runs and unpacks all files in the directory i am running it in.

my code looks like this so far....

#!/bin/sh -x
untgz() {                 #untgz function which takes one parameter
  gunzip -c <"$1" | tar xf -           #as its last argument
}


for I  in "$@";  do
  case $1  in
    *.tar.gz) ext=".tar.gz"; command=untgz;;
    *.tgz) ext=".tgz"; command=untgz;;
    *.tar.Z) ext=".tar.Z"; command=untgz;;
    *.zip) ext=".zip"; command=unzip;;
    *.ZIP) ext=".ZIP"; command=unzip;;
    *) echo "$0: cannot handle $1, skipping" >&2
        continue;;
  esac


  A=`basename "$1" "$ext"`
  mkdir "$A"
  mv "$1" "$A"
  cd "$A"
  $command "$1"
  mv "$1" ../
  cd ..
  mkdir store_folder
  mv "$1" store_folder
  cd ..
done

would i have to completely change the structure of my code or maybe change this line

for I  in "$@";  do

to

for I  in "*.";  do

or even could there be a way to type ./script .... folder i want to unpack...

Any ideas would be great thanks again

To loop over all files in the current directory, do

for I in *; do ...

You will want to tweak down the diagnostics so it doesn't complain about each and every file it doesn't know how to handle.

You still have $1 in a lot of places where you should now have $I (dollar capital I). Basically anywhere outside the function, you want $I (ih) not $1 (one).

The temporary move of the packed file into the directory where you extract it seems superfluous; just $command ../"$I" instead. If you change that, you will need to make some changes to the code where it moves the file to the store_folder, too.

tried changing

for I  in *;  do ...

but getting the following errors

 ./unpack
+ ...
./unpack: ...: not found
+ continue
+ ...
./unpack: ...: not found
+ continue
+ ...

my code looks like this have changed the $I's but the script is not handling the files in the current directory the way i want to...

what am i going wrong in my main program?

#!/bin/sh -x
untgz() {                 #untgz function which takes one parameter
  gunzip -c <"$1" | tar xf -           #as its last argument
}


for I  in *;  do ...
  case $1  in
    *.tar.gz) ext=".tar.gz"; command=untgz;;
    *.tgz) ext=".tgz"; command=untgz;;
    *.tar.Z) ext=".tar.Z"; command=untgz;;
    *.zip) ext=".zip"; command=unzip;;
    *.ZIP) ext=".ZIP"; command=unzip;;
   *)# echo "$0:  skipping" >&2
        continue;;
  esac


  A=`basename "$I" "$ext"`
  mkdir "$A"
  mv "$I" "$A"
  cd "$A"
  $command ../ "$I"
#  mkdir store_folder
#  mv "$I" store_folder
#  cd ..
done

thanks for all your help era

No, the ... was just to signal that you'd continue as before from that point on.

Just take out the three dots after "do" and fix the one remaining occurrence of $1 (in the case -- you should have case $I in --) and you should be fine. Oh, and you should either not move the file before unzipping it, and use $command "../$I" (there are no spaces between ../ and $I -- it means unzip the file $I in the parent directory), or move the file to the current directory, and omit the ../

You might want to put in some error checking, too: if the directory you want to create already exists, should it just go there anyway, or perhaps add a suffix to the directory name and try again, for example?

Similarly, if you run it on multiple files, store_folder will already exist at least after the first iteration, but it might also exist after a previous run. If nothing else, you probably want to avoid the warnings from attempting to create a directory which already exists. For that particular case, it's probably sufficient to simply check if it exists already, and only if not, try to create it.

test -d store_folder || mkdir store_folder

GOT IT FINALLY........... THANKS ERA YOU ARE A LEGEND................
hope i didnt drive you up the wall............. good job :slight_smile: