有两种拉链压缩文件运行diff的安全方法吗?

时间:2009-02-25 19:26:12

标签: compression diff

似乎这不是一个确定性的事情,或者有没有办法可靠地做到这一点?

13 个答案:

答案 0 :(得分:35)

如果你正在使用gzip,你可以这样做:

# diff <(zcat file1.gz) <(zcat file2.gz)

答案 1 :(得分:7)

可靠:解压缩,差异。

我不知道这个答案是否适合您的使用,但它确实有用。

答案 2 :(得分:5)

一般来说,你无法避免解压缩然后比较。不同的压缩器将导致不同的DEFLATEd字节流,当INFLATEd导致相同的原始文本时。您不能简单地将DEFLATEd数据相互比较。在某些情况下,这将失败。

但在ZIP方案中,为每个条目计算并存储了CRC32。因此,如果要检查文件,可以简单地比较与每个DEFLATEd流关联的存储CRC32,以及CRC32哈希的唯一性属性的警告。它可能符合您比较FileName和CRC的需要。

您需要一个ZIP库来读取zip文件并将这些内容公开为“ZipEntry”对象上的属性。 DotNetZip将为.NET应用程序做到这一点。

答案 3 :(得分:5)

zipcmp 比较zip存档zip1和zip2,并检查它们是否包含相同的文件,比较它们的名称,未压缩的大小和CRC。文件顺序和压缩的大小差异将被忽略。

sudo apt-get install zipcmp

答案 4 :(得分:2)

这不是特别优雅,但您可以使用Mac OS X开发人员工具附带的FileMerge应用程序来使用自定义过滤器比较zip文件的内容。

创建包含内容的脚本~/bin/zip_filemerge_filter.bash

#!/bin/bash
##
#  List the size, CR-32 checksum, and file path of each file in a zip archive,
#  sorted in order by file path.
##
unzip -v -l "${1}" | cut -c 1-9,59-,49-57 | sort -k3
exit $?

使脚本可执行(chmod +x ~/bin/zip_filemerge_filter.bash)。

打开FileMerge,打开“首选项”,然后转到“过滤器”选项卡。使用以下内容将项添加到列表: 扩展名:“zip”,过滤器:“〜/ bin / zip_filemerge_filter.bash $(FILE)”,显示:过滤,应用*:否。(我还为.jar和.war文件添加了文件管理器。)

然后使用FileMerge(或命令行“opendiff”包装器)来比较两个.zip文件。

这不会让您在zip存档中区分文件的内容,但是可以让您快速查看哪些文件只显示在一个存档中,哪些文件存在于两个存档中但具有不同的内容(即不同的大小和/或校验和) )。

答案 5 :(得分:1)

Beyond compare对此没有任何问题。

答案 6 :(得分:1)

实际上gzip和bzip2都带有专用工具。

使用gzip:

$ zdiff file1.gz file2.gz

使用bzip2:

$ bzdiff file1.bz2 file2.bz2

但请记住,对于非常大的文件,您可能会遇到内存问题(我最初是为了了解如何解决它们,所以我还没有答案)。

答案 7 :(得分:1)

用于zip文件的python解决方案:

import difflib
import zipfile

def diff(filename1, filename2):
    differs = False

    z1 = zipfile.ZipFile(open(filename1))
    z2 = zipfile.ZipFile(open(filename2))
    if len(z1.infolist()) != len(z2.infolist()):
        print "number of archive elements differ: {} in {} vs {} in {}".format(
            len(z1.infolist()), z1.filename, len(z2.infolist()), z2.filename)
        return 1
    for zipentry in z1.infolist():
        if zipentry.filename not in z2.namelist():
            print "no file named {} found in {}".format(zipentry.filename,
                                                        z2.filename)
            differs = True
        else:
            diff = difflib.ndiff(z1.open(zipentry.filename),
                                 z2.open(zipentry.filename))
            delta = ''.join(x[2:] for x in diff
                            if x.startswith('- ') or x.startswith('+ '))
            if delta:
                differs = True
                print "content for {} differs:\n{}".format(
                    zipentry.filename, delta)
    if not differs:
        print "all files are the same"
        return 0
    return 1

用作

diff(filename1, filename2)

它在内存中逐行比较文件并显示更改。

答案 8 :(得分:0)

WinMerge(仅限Windows)有很多features,其中一个是:

  
      
  • 使用7-Zip
  • 存档文件支持   

答案 9 :(得分:0)

我对这个简单的Perl脚本感到宽慰:diffzips.pl

它递归地区分原始zip中的每个zip文件,这对于不同的Java包格式特别有用:jar,war和ear。

zipcmp使用更简单的方法,并且不会递归到存档的拉链。

答案 10 :(得分:0)

我通常使用像@ mrabbit这样的方法,但运行2个解压缩命令并根据需要对输出进行区分。例如,我需要比较2个Java WAR文件。

$ sdiff --width 160 \
   <(unzip -l -v my_num1.war | cut -c 1-9,59-,49-57 | sort -k3) \
   <(unzip -l -v my_num2.war | cut -c 1-9,59-,49-57 | sort -k3)

导致输出如此:

--------          -------                                                       --------          -------
Archive:                                                                        Archive:
-------- -------- ----                                                          -------- -------- ----
48619281          130 files                                                   | 51043693          130 files
    1116 060ccc56 index.jsp                                                         1116 060ccc56 index.jsp
       0 00000000 META-INF/                                                            0 00000000 META-INF/
     155 b50f41aa META-INF/MANIFEST.MF                                        |      155 701f1623 META-INF/MANIFEST.MF
 Length   CRC-32  Name                                                           Length   CRC-32  Name
    1179 b42096f1 version.jsp                                                       1179 b42096f1 version.jsp
       0 00000000 WEB-INF/                                                             0 00000000 WEB-INF/
       0 00000000 WEB-INF/classes/                                                     0 00000000 WEB-INF/classes/
       0 00000000 WEB-INF/classes/com/                                                 0 00000000 WEB-INF/classes/com/
...
...

答案 11 :(得分:0)

我放弃了尝试使用现有工具并编写了一个适合我的小bash脚本:

#!/bin/bash
# Author: Onno Benschop, onno@itmaze.com.au
# Note: This requires enough space for both archives to be extracted in the tempdir

if [ $# -ne 2 ] ; then
  echo Usage: $(basename "$0") zip1 zip2
  exit
fi

# Make temporary directories
archive_1=$(mktemp -d)
archive_2=$(mktemp -d)

# Unzip the archives
unzip -qqd"${archive_1}" "$1"
unzip -qqd"${archive_2}" "$2"

# Compare them
diff -r "${archive_1}" "${archive_2}"

# Remove the temporary directories
rm -rf "${archive_1}" "${archive_2}"

答案 12 :(得分:0)

这里的许多解决方案要么只是检查CRC,以查看是否存在差异 ,它们是复杂的脚本,需要解压缩到磁盘,使用外部程序,还是需要除一种以外的特定压缩格式您在问(zcat does NOT work with zip)。

这是一种简单,易于阅读的方法,并且可以在显示bash显示文件内容之间差异的任何地方工作。

diff \
    <(zipinfo -1 "$zip1" '*' \
    | grep '[^/]$' \
    | sort \
    | while IFS= read -r file; do unzip -c "$zip1" "$file"; done \
    ) \
    <(zipinfo -1 "$zip2" '*' \
    | grep '[^/]$' \
    | sort \
    | while IFS= read -r file; do unzip -c "$zip2" "$file"; done \
    )

这会在内存中进行解压缩,而不是对磁盘进行解压缩,从而在差异化时从管道中释放数据(它不会解压缩,然后然后进行比较,因此不应使用太多内存)。
是否要更改差异选项以忽略空白或并排使用?将diff更改为diff -wgvimdiff(此文件会将所有文件保留在内存中)等。
假设您只想比较.js文件?将*更改为*.js
只想查看一个或另一个缺少的文件名?删除while行,它就不会再解压缩了。

容易

它甚至可以安全地处理(跳过并记录到stderr)文件名中,并使用“非法”字符(例如换行符和反斜杠)。
不会比这更“安全”。

slm的答案非常适合返回不同的文件(不显示差异),甚至根本不解压缩,这很好。如果出于某种原因,您希望在CRC之上,而在CRC之上,则可以在| sha512sum之前添加; done,得到“两全其美”:P


类似地,比较存档和真实目录相对容易:

diff \
    <(zipinfo -1 "$zip" '*' \
    | grep '[^/]$' \
    | sort \
    | while IFS= read -r file; do unzip -c "$zip" "$file"; done \
    ) \
    <(find "$directory" -type f -name '*' \
    | sort \
    | while IFS= read -r file
      do
          printf 'Archive:  %s\n  inflating: %s\n' "$directory" `echo $file | sed "s|$directory/||"`
          cat "$file"
          echo
      done \
    )

或者,仅忽略目录中的文件,基本上是unzip -o -d "$directory"的便捷试运行:

diff \
    <(zipinfo -1 "$zip" '*' \
    | grep '[^/]$' \
    | sort \
    | while IFS= read -r file; do unzip -c "$zip" "$file"; done \
    ) \
    <(zipinfo -1 "$zip" '*' \
    | grep '[^/]$' \
    | sort \
    | while IFS= read -r file
      do
          printf 'Archive:  %s\n  inflating: %s\n' "$directory" "$file"
          cat "$directory/$file"
          echo
      done \
    )

Windows?抱歉。尽管脚本很简单,并且可以轻松移植到[语法上]出色的Powershell,但它无法正常工作。本地cmdlet only extracts to disk和MS still 尚未修复broken binary data piping in PS,因此您也不能以这种方式“安全”使用外部zip.exe

其他人也使用.NET API directly做过类似的事情,但是它变得不再是优雅的移植,而更多地是在.NET中的重新实现:|


关于前面提到的“非法文件名”的说明:
如果您希望它与这些协同工作,实际上并不是很困难;您只需要将$file$(echo "$file" | sed 's/\\/\\\\/g;s/\^J/\n/g;s/\^M/\r/g')交换即可。

添加其他ctrl chars时会遇到它们。

原因是由于某种原因,即使zipinfo在其中以\n显示带有^J的文件名时,它也不会接受{{1 }},只有原始的!即使它可以使用unzip -^提取到那些非法文件名,也根本无法通过unzip获得这些原始文件名。因此,您需要从安全的,无法使用的文件中构建原始的非法文件名,以为diff引用它们:(
如果执行此操作,请注意,无法从字面上区分zipinfo和显示为^J的{​​{1}},并且该zip不支持\n或{{1 }}中的所有文件名。


作为奖励;您可以将所有这些差异直接写入归档文件,并将其全部保存在与原始文件匹配的文件夹层次结构中,而不必试图一次读取所有差异。

^J

虽然不是一个漂亮的脚本,但是现在您可以在您选择的gui存档器中将其打开,或者执行/来查看与该文件的区别,或者如果找不到该文件,则被“找不到”问候差异,这在实践中更漂亮。