也许我最好先从用例开始。
我有一个包含许多文件的大型 zip 文件,我怀疑它最初是在低压缩级别下压缩的。
我想从该文件创建一个新的 zip 文件(或者不是新的,希望是就地的),以确保它被很好地压缩(-9
),而无需先将其解压缩到磁盘(出于各种原因,例如大小和挂载的 NFS)。因此,虽然我可以这样做
mkdir tmp/ && unzip -d tmp/ A.zip && cd tmp/ && zip -r -9 ../B.zip *; rm -rf tmp/ A.zip
这将[暂时]产生大量数据(解压文件并在被删除B.zip
之前存在A.zip
)。
我真正想要的是类似这样的东西:
rezip -9 A.zip
或者,选择一个B.zip
:
unzip -<output to stdout> A.zip | zip -9 B.zip && rm -f A.zip
但我意识到这可能实际上是不可能的,因为管道将用于多个文件,而文件名等内容将会丢失。
因此,我想知道是否有办法在每个文件上执行一些代码,例如find -exec <command>
unzip A.zip -exec zip -r B.zip && rm -f A.zip
但同样,不确定这将如何工作,或者,如果这不是由unzip
/实现的zip
,是否存在可以执行的命令。
我已经将我目前仍在使用临时的单个文件解决方法作为答案,因为它可能会对某些人有所帮助,尽管它不被接受,因为它没有回答问题不允许临时工
答案1
已经有一段时间了,但下面是具体操作方法。
适用于名称中带有空格的文件和子目录。
zipinfo -1 A.zip | while read filename
do
unzip -p A.zip "$filename" | zip -9 A.zip -
zip --delete A.zip "$filename"
printf "@ -\n@=$filename\n" | zipnote -w A.zip
done
答案2
我找到了一个更好的解决方案:
unzip -p original.zip | zip -9 new.zip -
unzip -p 告诉 unzip 将输出发送到 stdout。zip 命令的最后一个破折号告诉它从 stdin 读取。
如果确实需要,您仍然需要将 new.zip 重命名为 original.zip。
答案3
开源Zip-Ada项目正好有这样一个 rezip 工具。
- 获取/下载源代码
- 使用命令构建
gnatmake -P zipada
(您可以通过 apt 或 yum 获取 GNAT) - 您现在有一个具有以下选项的二进制 rezip:
。
Usage: rezip [options] archive(s)[.zip]
Options: -defl : repack archive only with the Deflate
subformat (most compatible)
-fast_dec : repack archive only with fast decompressing subformats
-int : use internal Zip-Ada algorithms only, no external call
-touch : set time stamps to now
-lower : set full file names to lower case
-del_comm : delete comment
-comp : compare original and repacked archives (paranoid mode)
-rs=n : loop many times over a single compression approach
having randomization, and keep optimum when size is
stable after n attempts in a row
首先,我建议使用-defl
和-int
选项(值得注意的是,-defl
将使生成的 Zip 文件与旧工具保持兼容unzip
)。
例如,命令如下./rezip -int -defl -comp A.zip