如何从Unix命令行递归解压缩目录及其子目录中的档案?


68

unzip命令没有用于递归解压缩存档的选项。

如果我具有以下目录结构和档案:

/母亲/Loving.zip
/ Scurvy / Sea Dogs.zip
/Scurvy/Cures/Limes.zip

我想将所有档案解压缩到与每个档案同名的目录中:

/母亲/爱人/1.txt
/母亲/Loving.zip
/ Scurvy / Sea Dogs / 2.txt
/ Scurvy / Sea Dogs.zip
/Scurvy/Cures/Limes/3.txt
/Scurvy/Cures/Limes.zip

我会发出什么命令?

重要的是,不要让其中包含空格的文件名阻塞。


如何处理超过260个字符的长文件路径...:superuser.com/a/1263234/439537
Andrew

Answers:


124

如果要将文件解压缩到相应的文件夹中,可以尝试此操作

find . -name "*.zip" | while read filename; do unzip -o -d "`dirname "$filename"`" "$filename"; done;

适用于可以处理高I / O的系统的多进程版本:

find . -name "*.zip" | xargs -P 5 -I fileName sh -c 'unzip -o -d "$(dirname "fileName")/$(basename -s .zip "fileName")" "fileName"'

1
这是正确的做法,因为它保留了完整的目录结构!
kynan 2014年

6
注意-根据要求,这不会将文件提取到与存档名称相同的目录中。
埃莱

1
我不知道xargs具有多核支持!💯–
chuckrector

它不能解压缩到单独的文件夹,这意味着它不能完全回答所提出的问题。另外,如果给定文件夹中的多个zip文件包含相同名称的文件,例如,results.log当您尝试将同一文件的多个副本解压缩到一个文件夹中时,这将导致问题。我正在Cygwin上尝试这个。看起来该-d "'dirname...零件应该可以解决这个问题,但是对我来说不起作用。
SSilk's

2
出于某种原因,第一个示例对我来说与emlai一样存在相同的问题,所有解压缩的文件都放在zip文件所在的目录中。但是,第二个示例(多线程方法)不仅运行速度更快,而且每个zip都放入文件的文件位于以zip文件命名的目录中。完善!
MoTLD

37

这是一个将所有zip文件提取到工作目录并涉及find命令和while循环的解决方案:

find . -name "*.zip" | while read filename; do unzip -o -d "`basename -s .zip "$filename"`" "$filename"; done;

6
注意-这会将所有zip文件解压缩到工作目录中!而您要求相对解压缩。参见Vivek的答案
Jossef Harush

嗯,似乎没有将它们全部提取到工作目录AFAICT。我正在使用OS X El Capitan 10.11.6的UnZip 5.52。结果似乎与Vivek相同。但是接受他的答案,因为它还显示了如何利用多个内核!
chuckrector

27

一种解决方案,可以正确处理所有文件名(包括换行符),并提取到与文件位于同一位置的目录中,而无需删除扩展名:

find . -iname '*.zip' -exec sh -c 'unzip -o -d "${0%.*}" "$0"' '{}' ';'

请注意,您可以.jar使用添加它们-o,从而轻松地处理更多文件类型(例如),例如:

find . '(' -iname '*.zip' -o -iname '*.jar' ')' -exec ...

到目前为止,我发现的唯一可以正常工作的解决方案
kostja,

这是迄今为止唯一
Rob

1
建议更改为“ find。-iname ...”以解压缩.ZIP和.zip文件
Luke Rehmann

4

您可以在单个命令行中将find与-exec标志一起使用来完成此工作

find . -name "*.zip" -exec unzip {} \;

1
这会将所有内容解压缩到当前目录中,而不是相对于每个子目录。它也不会解压缩到与每个档案同名的目录中。
chuckrector

我假设让-d正确作为读者的练习。该读者需要注意-exec仅允许在命令中使用{} -通过调用sh并将{}分配给变量来解决此问题。
史蒂夫·杰索普

还要注意,-execdir有时比-exec更可取。在这种情况下,我认为这并不重要。
史蒂夫·杰索普

我的发现(GNU findutils 4.4.0)让我多次使用{} ... cloud @ thunder:〜/ tmp / files $ find。-exec echo {} {} \; 。。./a ./a ./b ./b ./c ./c ./d ./d ./e ./e ./f ./f ./g ./g
Sam Reynolds

我的答案,它采用-exec包括-d。请注意,在调用时必须小心,sh以确保"; rm -rf /;不会执行类似邪恶的文件名。
罗宾斯特2014年

4

这可以按照我们的要求完美运行:

解压缩文件:

find . -name "*.zip" | xargs -P 5 -I FILENAME sh -c 'unzip -o -d "$(dirname "FILENAME")" "FILENAME"'

上面的命令不会创建重复的目录。

删除所有zip文件:

find . -depth -name '*.zip' -exec rm {} \;

3

像是使用-r标志的gunzip吗?

递归地移动目录结构。如果在命令行上指定的任何文件名都是目录,则gzip将下降到该目录并压缩在该目录中找到的所有文件(如果是gunzip,则将其解压缩)。

http://www.computerhope.com/unix/gzip.htm


2
他专门讲的是zip文件,而不是gzip文件。
dvorak

0

如果您使用的是cygwin,则basename命令的语法略有不同。

find . -name "*.zip" | while read filename; do unzip -o -d "`basename "$filename" .zip`" "$filename"; done;

0

我意识到这很老了,但是当我正在寻找类似问题的解决方案时,它是Google上的热门产品之一,因此我将在此处发布我的工作。我的场景略有不同,因为我基本上只是想完全炸开一个jar以及其中包含的所有jar,所以我编写了以下bash函数:

function explode {
    local target="$1"
    echo "Exploding $target."
    if [ -f "$target" ] ; then
        explodeFile "$target"
    elif [ -d "$target" ] ; then
        while [ "$(find "$target" -type f -regextype posix-egrep -iregex ".*\.(zip|jar|ear|war|sar)")" != "" ] ; do
            find "$target" -type f -regextype posix-egrep -iregex ".*\.(zip|jar|ear|war|sar)" -exec bash -c 'source "<file-where-this-function-is-stored>" ; explode "{}"' \;
        done
    else
        echo "Could not find $target."
    fi
}

function explodeFile {
    local target="$1"
    echo "Exploding file $target."
    mv "$target" "$target.tmp"
    unzip -q "$target.tmp" -d "$target"
    rm "$target.tmp"
}

请注意,<file-where-this-function-is-stored>如果要将其存储在一个文件中,这是必需的,因为我碰巧要为非交互式shell读取该文件。如果将函数存储在非交互式shell上加载的文件中(例如,.bashrc我相信),则可以删除整个source语句。希望这会对某人有所帮助。

有点警告-explodeFile也会删除压缩文件,您当然可以通过注释掉最后一行来更改它。


0

另一个有趣的解决方案是:

DESTINY=[Give the output that you intend]

# Don't forget to change from .ZIP to .zip.
# In my case the files were in .ZIP.
# The echo were for debug purpose.

find . -name "*.ZIP" | while read filename; do
ADDRESS=$filename
#echo "Address: $ADDRESS"
BASENAME=`basename $filename .ZIP`
#echo "Basename: $BASENAME"
unzip -d "$DESTINY$BASENAME" "$ADDRESS";
done;

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.