find . -type f -name "*.zip" -print0 | xargs -0 -I @ bash -c 'cd $(dirname "@"); 7z x -r -spe -tzip $(basename "@")'
find . -type f -name "*.7z" -print0 | xargs -0 -I @ bash -c 'cd $(dirname "@"); 7z x -r -spe $(basename "@")'
This uses the dirname
command to extract the directory of a file and the basename
command to extract the filename itself.
To make sure this is secure and works with filenames with spaces in them, we print null-terminated strings (-print0
coupled with xargs -0
) instead of using -exec
. xargs
will take all lines from stdin
and invoke the given command line, which here calls bash
to do a cd
to the directory followed by the unarchive command on the filename itself.
If you don't have the dirname
/basename
utilities installed, and it appears you do not, you can also use this (note: this does not work for find /
):
find . -type f -name "*.zip" -print0 | xargs -0 -I @ bash -c 'fname="@"; base="${fname##*/}"; dir="${fname%/${base}}"; cd "${dir}"; 7z x -r -spe -tzip "${base}"'
find . -type f -name "*.7z" -print0 | xargs -0 -I @ bash -c 'fname="@"; base="${fname##*/}"; dir="${fname%/${base}}"; cd "${dir}"; 7z x -r -spe "${base}"'
This is a little more complicated. First we need to assign the value xargs
reads (@
, specified by the xargs
-I
option) to a variable for manipulation. Note: This will not work if the filename contains quotes or escape sequences, which I trust it will not. The ${fname##*/}
will delete everything up to and including a final slash on the left-hand side, thus removing the directory and forming a "basename". This gets assigned to $base
. The directory is everything that is not the base, so we remove the base from the original filename (${fname%/${base}}
) and call that $dir
. We change directory to the directory of interest and unarchive the basename.
[eje@localhost recurse-cd]$ ls -R
.:
a b
./a:
a a.zip b c
./a/a:
a-a.zip
./a/b:
a-b.zip
./a/c:
a-c.zip
./b:
a b b.zip
./b/a:
b-a.zip
./b/b:
b-b.zip
[eje@localhost recurse-cd]$ find . -type f -name "*.zip" -print0 | xargs -0 -I @ bash -c 'cd $(dirname @); unzip $(basename @)'
Archive: a-a.zip
extracting: a-a
Archive: a-b.zip
extracting: a-b
Archive: a-c.zip
extracting: a-c
Archive: a.zip
extracting: a-file
Archive: b-a.zip
extracting: b-a
Archive: b-b.zip
extracting: b-b
Archive: b.zip
extracting: b-file
[eje@localhost recurse-cd]$ ls -R
.:
a b
./a:
a a-file a.zip b c
./a/a:
a-a a-a.zip
./a/b:
a-b a-b.zip
./a/c:
a-c a-c.zip
./b:
a b b-file b.zip
./b/a:
b-a b-a.zip
./b/b:
b-b b-b.zip