简体   繁体   中英

Recusively change directory and unzip files - linux

I have several compressed files that have embedded/nested compressed files within sub-directories using various compression formats, eg 7z, zip, tar. If I use the following approach (see below) to recursively uncompress them, all files are uncompressed to root directory from which I initiated the command. Since none of these compressed files are under my control (I didn't create them), they did not preserve the original directory structure when they archived them. So I'm forced to find a why to do a 'cd' change directory to the sub-directory that they reside under before uncompressing them.

Here is how I'm going about it now...

$ find . -type f -name "*.zip" -exec 7z x -r -spe -tzip '{}' \;
$ find . -type f -name "*.7z" -exec 7z x -r -spe '{}' \;

How do I go about doing a 'cd' given where the compressed file was found and uncompressing at the location? I image that some sort of 'while' loop will be needed with addition checks and commands.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM