如何“解压缩” zip文件?


52

我将一个zip文件解压缩到一个非空文件夹中。该zip文件包含大量文件和深入的层次结构,它们与目标目录的现有树合并。如何删除通过解压缩创建的文件和目录,而不破坏已经存在的文件和目录?当然,我仍然有合并的zip文件,因此信息就在那里。


嗯,谢谢您的接受,但这确实是@jjin的想法。我不知道的lq选项unzizp,只是在他的主要回答周围添加了一些经典的* nix技巧。
terdon

没关系,我一点也不在乎。无论如何,我添加了自己的不同版本的空白处理。
jjlin

@terdon是的...我也赞成jjlin的答案,但我只能接受一个答案。
mafp

为了将来参考,请始终对不熟悉的任何格式的存档执行以下操作之一:1)将其提取到一个空目录中,或2)在提取之前先列出它(解压缩-l),这样您就可以查看它是否令人讨厌。没有顶级目录的归档文件,其下的所有格式均为错误格式。用焦油做完之后,它们实际上称为焦油炸弹,因此我想这可以称为压缩炸弹。

@Joe有它的用途。例如,LaTeX软件包可以采用某种foo.tds.zip形式。这些zip合并为TEXMF树,这非常方便。但是,如果您想删除这样的软件包,您将面临我描述的问题。
mafp

Answers:


28

吉林的答案就是要走的路。我只想为目录添加一些选择:

  • 删除所有提取的文件,没有目录

    unzip -lqq file.zip | gawk -F"  " '{print $NF;}' |
      while IFS= read -r n; do rm "$n"; done
    
  • 仅删除提取的文件和目录

    unzip -lqq file.zip | gawk -F"  " '{print $NF;}' |
      while IFS= read -r n; do rm "$n"; done; rmdir *
    

    不带任何选项,rmdir仅删除空目录,它将只保留文件和非空文件夹,因此您可以在上安全地运行它*

  • 删除提取的所有内容,但在每次删除之前提示您确认:

    unzip -lqq file.zip | gawk -F"  " '{print $NF;}' |
      while IFS= read -r n; do rm -ri "$n"; done; rmdir *
    

    -i标志将rm在每次删除之前提示您,您可以选择是或否。

  • 删除所有提取的内容,包括目录:

    unzip -lqq file.zip | gawk -F"  " '{print $NF;}' |
      while IFS= read -r n; do rm -rf "$n"; done
    

使用find:可以轻松删除空目录,find * -depth -type d -exec rmdir {} +并忽略所有Directory not empty消息。将其缩短find * -type d -delete为打开-delete选项可能是合法的,-depth但我尚未确认-delete不会删除非空目录。
Adrian Pronk

@AdrianPronk,它不会:find: cannot delete './foo': Directory not empty
2013年

28

您可以使用unzip -lqq <filename.zip>列出zip文件的内容。不过,这将包括一些您需要过滤掉的无关信息。这是一个对我有用的命令:

unzip -lqq file.zip | awk '{print $4;}' | xargs rm -rf

awk命令仅提取文件和目录的名称。然后将结果传递xargs给删除所有内容。我建议先执行命令的空运行(即,省略xargs rm -rf零件)以确保结果正确。

上面的命令在处理带有空格的路径时会遇到问题。此(更复杂的)版本应解决以下问题:

unzip -lqq file.zip | awk '{$1=$2=$3=""; sub(/ */, "", $0); printf "%s%s", $0, "\0"}' | xargs -0 rm -rf

这已经非常接近我的想法,但是unzip -lqq还列出了zip中包含的目录。现在,我将仅保留所有目录。如何删除树中所有空目录可能是一个后续问题。
mafp

@mafp关于目录,这是一个很好的观点。您可以添加grep -v '/$'到管道中以跳过删除目录(所有目录都带有斜杠AFAICT)的操作。
jjlin

@terdon实际上,我认为问题始于awk,因为仅打印$ 4不会打印完整路径。
jjlin

我不认为您应该使用-rrm选项:这似乎在麻烦,特别是与该-f选项结合使用时。-f在这种情况下,我根本不会使用该选项。
Adrian Pronk

1
@jjlin:grep -v '/$'仅忽略ZIP文件中的目录条目。它们仍将包含条目,这些条目是ZIP文件中的纯文件,但是是目标文件夹中的预先存在的目录。出于这个原因,明智的做法是省略-r
Adrian Pronk

11

使用switch -Z1,解压缩将在每行中仅列出一个文件(没有其他内容)。

这样,您可以使用

unzip -Z1 | xargs -I {} rm '{}'

删除从zip文件提取的所有文件。

命令

unzip -Z1 | xargs -I {} rm -rf '{}'

也会删除目录,但您必须小心。如果在解压缩zip文件之前目录已经存在,则这些目录中所有先前存在的文件也将被删除。


如果您仍然要重新提取zip文件,则可以确保使用另一种方法来处理奇怪的文件名。

首先将zip文件解压缩到您最初要解压缩的位置:

unzip file.zip -d elsewhere

现在,转到错误地提取文件的目录,然后执行以下命令:

find elsewhere -type f -printf "%P\0" | xargs -0 -I {} rm '{}'
  • -type f 仅查找文件(无目录)。

  • %P\0是相对路径(不带elsewhere/),后跟一个空字符。

  • -0使xargs用空字符分隔行。从理论上讲,这是更可靠的,因为文件名可以包含换行符。


要处理剩余的目录,可以执行以下命令:

find -type d -exec rmdir -p {} \; 2> /dev/null
  • -type d 仅查找目录。

  • -exec rmdir -p {} \;rmdir -p {}对找到的每个目录执行。

    {}是已找到的目录,并且该-p开关使rmdir也删除其空的父目录。

  • 2> /dev/null 禁止尝试删除非空或先前删除的目录时出现的错误消息。


相关手册页:


+1使我阅读zipinfo的手册页。
terdon

好吧,天哪,这使它变得容易一些。:)
jjlin

2

这是一个更简单,更安全(我认为)的解决方案

zip -m getmeoutofhere.zip `unzip -lqq myoriginalzipfile.zip`
rm getmeoutofhere.zip

这是做什么的:反引号解压缩命令将生成原始文件中的内容的列表。

zip -m然后将使用该列表将每个添加到getmeoutofhere.zip中,并将其从原始目录中删除(因此从理论上讲,它应与myoriginalfile.zip保持一致。

缺点是,解压缩-lqq会产生一些额外的文本,日期,时间,文件大小等。这将导致zip -m产生错误消息,但这不会产生任何影响(除非您不太可能出现相同文件的情况名称)。

请注意,这不会删除原始解压缩过程中创建的任何目录。


有趣的方法,将进一步探索。
mafp

1

如果您提取文件以使存档中的修改时间戳记未保留在提取的副本中(而是提取的文件具有其通常的修改时间),那么解决此问题的正确方法是通过修改时间。所有提取的文件都具有比该目录中最新修改的现有文件新的修改时间戳。

这是一个简单的情况。

假设至少有24小时未触摸当前目录中的任何现有文件。因此,过去24小时内修改的所有内容都会从zip文件中删除。

$ find . -mtime -1 -print0 | xargs -0 rm

这样也可以找到一些目录,但rm将其保留下来。他们可以通过第二遍处理:

$ find . -mtime 1 -type d -print 0 | xargs -0 rmdir

zip修改了最近修改的所有目录。如果rmdir成功删除它们,则表示它们为空。zip涉及的空目录可能是由它创建的:即来自存档。我们不能100%确定。解压缩作业可能会将一些文件放到现有的空目录中。

如果find24小时的粒度不足以完成这项工作,因为树中的文件太新了,那么我接下来考虑一下简单的事情:假设解压缩工作没有将任何内容放入现有子目录中。也就是说,所有解压缩的文件要么是顶层文件,要么是以前不存在的新子目录,因此该子目录仅包含来自zip的内容。然后:

# list directory in descending order of modification time
$ ls -1t > filelist  # descending order of modification time

现在,我们filelist在文本编辑器中打开,并确定列表中的第一项不是来自zip的。我们删除该条目及其后的所有其他内容。剩下的是来自zip的文件和目录。首先,我们以视觉方式检查名称中的空格等问题以及需要转义的引号的出现。然后,如有必要,我们可以在所有内容周围添加引号:以下假定您使用Vim:

:%s/.*/"&"/

然后将其全部加入一行:

:%j

现在插入rm -rf它的前面:

Irm - rf<ESC>

作为shell命令在光标下方运行该行:

!!sh<Enter>

绝对地,由于存在擦除已存在文件的风险或由于文件名问题而搞砸的风险,因此我不会自动执行此任务的步骤。

如果您打算采用明显的方法来获取zip中的路径列表,然后将其捕获到文件中,请仔细查看并在进行任何必要的编辑后将其转换为删除内容。

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.