在一个目录中为每个zip文件创建一个专用文件夹并提取zip文件。

64

如果我选择一个zip文件并右键单击“解压缩到此处”,则会创建一个名为zip文件名的文件夹,并将整个zip文件的内容提取到其中。

然而,我想通过shell转换多个zip文件。但是当我这样做时

unzip filename.zip

文件夹"filename"没有被创建,但所有的文件都被提取到当前目录。

我已经查看了参数,但没有这样的参数。 我也尝试过。

for zipfile in \*.zip; do mkdir $zipfile; unzip $zipfile -d $zipfile/; done

但需要使用sed移除2. $zipfile和4. $zipfile的.zip扩展名。 如果我这样做:

for zipfile in \*.zip; do mkdir sed 's/\.zip//i' $zipfile; unzip $zipfile -d sed 's/\.zip//i' $zipfile/; done 

它不起作用。

我该如何正确地替换$zipfile.zip扩展名?

除了使用shell脚本,是否有更简单的方法?

7个回答

96

unzip file.zip -d xxx命令将会把文件解压到目录xxx中,如果该目录不存在,则会被创建。您可以查看man页面以获取更多详细信息。

下面的awk行应该能够完成这项工作:

ls *.zip|awk -F'.zip' '{print "unzip "$0" -d "$1}'|sh

请看下面的测试

注意,我删除了末尾的|sh,因为我的压缩文件是假的归档文件;我只是想在这里展示生成的命令行。

kent$  ls -l
total 0
-rw-r--r-- 1 kent kent 0 Nov 12 23:10 001.zip
-rw-r--r-- 1 kent kent 0 Nov 12 23:10 002.zip
-rw-r--r-- 1 kent kent 0 Nov 12 23:10 003.zip
-rw-r--r-- 1 kent kent 0 Nov 12 23:10 004.zip
-rw-r--r-- 1 kent kent 0 Nov 12 23:10 005.zip
-rw-r--r-- 1 kent kent 0 Nov 12 23:10 006.zip
-rw-r--r-- 1 kent kent 0 Nov 12 23:10 007.zip

kent$  ls *.zip|awk -F'.zip' '{print "unzip "$0" -d "$1}'
unzip 001.zip -d 001
unzip 002.zip -d 002
unzip 003.zip -d 003
unzip 004.zip -d 004
unzip 005.zip -d 005
unzip 006.zip -d 006
unzip 007.zip -d 007

但是我不明白的是为什么$0包含完整的文件名而$1不包含“.zip”扩展名? - creativeDev
4
@jack,我将“.zip”定义为字段分隔符(FS)。因此,“001.zip”将被分成两个部分,“001”和“”。$1是第一个字段,$0是整行,对于awk而言,字段从1开始计数,而非从0开始。 - Kent
2
但如果 xxx 是嵌套目录路径,它将失败并发出“无法创建提取目录”的消息。当然,我可以编写一个 shell 脚本并检查中间目录是否存在,然后提前创建它,但我希望只使用 unzip 命令一次完成。 - Scott Chu
6
这个命令很好用,但如果文件名中有空格就会无法运行。为了处理空格,你需要执行ls *.zip | awk -F'.zip' '{print "unzip \""$0"\" -d \""$1"\""}' | sh - KurtPreston
3
不应解析ls输出。在我的系统上行不通,因为ls默认启用了-lah选项。ls是一款交互式工具。使用路径名扩展。 - Nobody
请勿使用 ls 输出进行任何操作。ls 是一个交互式查看目录元数据的工具。尝试使用代码解析 ls 输出是错误的。通配符更简单且正确:for file in *.txt。请阅读 http://mywiki.wooledge.org/ParsingLs。 - Rany Albeg Wein

64

"提取这里"仅仅是您使用的任何unzip包装器的一个功能。 unzip将只会提取档案中实际存在的内容。如果您拥有符合POSIX标准的shell,那么可能没有比一个shell脚本更简单的方法了。但是,不需要使用sedawk等工具执行此操作:

for f in *.zip; do unzip -d "${f%*.zip}" "$f"; done

(你不应该转义*,否则路径名扩展将不会发生。)请注意,如果ZIP归档文件包含一个目录,例如Eclipse归档文件(它们始终包含eclipse/),则您最终可能会在任何情况下都得到./eclipse*/eclipse/eclipse.ini。在unzip之前添加echo以进行试运行。


2
@jack,这个答案应该被接受。你不应该使用ls输出来做任何事情。ls是一个交互式查看目录元数据的工具。任何试图用代码解析ls输出的尝试都是错误的。通配符(Globs)更简单且正确:for file in *.zip。请阅读解析ls - Rany Albeg Wein
注意:您能否改进您的答案(绝对回答了如何在专用文件夹中提取多个zip文件的原始问题)并包括“如何创建文件夹以将单个zip文件提取到其中?”这种情况? - el-teedee
我有答案(一半),我对别名的工作原理没有很好的概念,它就像C/C++中的include一样工作,无论别名名称持有什么,都将被复制到其位置,并且如果在结尾(或任何地方)写入$@、$1等,则会被省略,因此例如:alias lsg='ls | grep ' 然后 lsg zip,这将相当于 ls | grep zip。而 alias ech='echo "$1" ' ech ok yes again | 你期望只显示 ok,但结果就像 $1 不存在一样。就像变量被忽略一样。如果有人能解释得更多,那就太好了。非常感谢。 - Mohamed Allal
@el-teedee 只需将第一个 *.zip 替换为您想要提取的 ZIP 存档的路径即可 :) - PointedEars
"${f%*.zip}"中的替换操作会删除最短的正则表达式模式(https://tldp.org/LDP/abs/html/string-manipulation.html)。a)在正则表达式中,前导的`*`没有意义,似乎被静默忽略了;b)对于像`.zip`这样的固定字符串,使用正则表达式有些过度。在Bash中,您可以使用`${f:0:(-4)}`来删除最后四个字符。 - IBBoard
显示剩余9条评论

13

p7zip 是 7zip 的命令行版本,可以完成相同的工作。

7z x '*.zip' -o'*'

在Debian和Ubuntu上,您需要安装p7zip-full软件包。


简单而优雅!太棒了!谢谢! - coverboy

7
在开关后添加文件夹名称和斜杠,例如:
unzip -d newfolder/ zipfile.zip

zip将创建文件夹“newfolder”并将存档解压缩到其中。

请注意,尾随斜杠不是必需的,但我猜这只是出于旧习惯(我使用Debian和Ubuntu)。


对我来说,斜杠是必需的,否则命令会抱怨找不到这样的文件夹。 - Marty

1
< p > aunpack 是 atool 中默认可以对各种类型的归档文件进行解压缩的工具。


1
在Termux bash中,我最终得到了这个结果,测试了文件名中的空格和点。它只会删除文件夹名称的最后一个文件扩展名和点。这假定zip文件具有.zip扩展名,否则它将无法工作。在意识到引号只需要作为普通引号存在时,我试图转义引号或在引号前使用单引号而变得疯狂。在zip命令前加上echo,或添加-l以审核命令。由于某种原因,-d的位置对于此实现似乎很重要。
for f in *.zip; do unzip "$f" \-d "./${f%.*}/"; done; echo -end-

-1
  1. 打开终端并定位到00*文件
cat filename.zip.00* > filename.zip

等到进程结束,这取决于文件大小。

  1. 文件合并过程已完成,现在可以运行输出文件。
unzip filename.zip

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接