views:

277

answers:

5

I've seen loops to unzip all zip files in a directory. However, before I run this, I would rather make sure what I'm about to run will work right:

for i in dir; do cd $i; unzip '*.zip'; rm -rf *.zip; cd ..; done

Basically I want it to look at the output of "dir" see all the folders, for each directory cd into it, unzip all the zip archives, then remove them, then cd back and do it again until there are no more.

Is this something I should do in a single command or should I consider doing this in Perl?

A: 

Use * to get the contents of the current directory:

for i in *; do cd "$i"; ...etc... ; done

You should also make sure that something is a directory before trying to cd into it. In bash this is done as follows:

if [ -d "$i" ]; then ...etc... ; fi

You should also be careful to check that the unzip operation succeeded before deleting the zip file. There is no need to use recursive force delete for removing a single file.

I think this is pushing the limit for what it sensible to do one a single line. Put it into a shell script and format it on multiple lines.

Mark Byers
+1  A: 

You should be fine in general. For debugging just prefixing all commands in the loop with echo will tell you how the variables expand. You can also always use rm -i to be asked instead of making rm go ahead with anything with -f.

The single quotes in your unzip call will only unzip files named *.zip, i.e. the * will not expand. Use double quotes instead (or no quotes at all).

Your loop now assumes that all directories $i are directly under the current one. A more general solution would be to use the directory stack (pushd and popd);

for i in $dir; do
  pushd $i
  unzip *.zip
  rm -rf *.zip
  popd
done
honk
i had to change the first from for i in dir to for i in *, it unziped stuff, however had issues with files that contained spaces in the name.
helpwithshell
@help: I implicitly assumed that `dir` was a variable when it wasn't so my example was wrong. Fixed here and now assumes that `dir` is a variable holding the directory listing.
honk
A: 

partly tested:

find . -type d -exec  sh -c "cd \"{}\" ;  unzip \"*.zip\" ; rm \"*.zip\"  "  \;

executes the cd/unzip/rm chain for every subdir.

Patrick
So far this works, where the other answer couldn't take stuff like "Folder Name With a Space", however its not removing the .zip archives.
helpwithshell
I think you have to use `find ... | xargs -0 ...` to handle whitespace , or do escaped quotes do the job?
aaa
In the end I Was able to modify your command line to:find . -name "*.zip" -exec sh -c "rm -rf \"{}\" " \;to remove all teh left over .zip archives. thanks
helpwithshell
for the deletion of the zip files, you can use `find . -name "*.zip" -exec rm -rf {} \;` (you don't have to use sh -c ... when you issue only one command)
Patrick
+1  A: 

A few points adding to what others have said:

  • If you put a parameter in single quotes, like in '*.zip', the shell won't do wild card expansion and unzip will try to process an archive that is literally called *.zip
  • Don't use -r as a parameter to rm if you just want to remove single files. That's just unnecessarily dangerous since any recursive deleting of subdirectories would probably not be intended.
  • You should check the return code of unzip and only remove the zip file if unzipping was successful (for example you could say if unzip *.zip; then rm *.zip; fi, or even use a explicit for loop to process the zip files one by one)
sth
A: 

When you want to know if a something will work but are wary of it's destructive consequences, do a little at a time:

Make sure you get the right files:

for i in dir; do cd $i; echo $i; cd ..; done

Make sure you can unzip the files before you delete the files:

for i in dir; do cd $i; unzip '*.zip'; cd ..; done

And, before you do the full monty, make a back-up of your directory.

brian d foy