如何检查两个压缩文件是否相等?


11

我试图通过简单地将数据转储到文本文件中来进行“哑”备份时节省空间。我的备份脚本每天执行一次,如下所示:

  1. 创建一个以备份日期命名的目录。
  2. 将一些数据转储到文本文件中"$name"
  3. 如果文件有效,则将其gzip压缩:gzip "$name"。否则,rm "$name"

现在,如果前一天还提供了相同的数据,我想添加一个附加步骤以删除文件(并创建符号链接或硬链接)。

起初我想到使用md5sum "$name",但这是行不通的,因为我还存储了文件名和创建日期。

是否gzip可以比较两个压缩文件并告诉我它们是否相等?如果gzip没有这样的选择,还有其他方法可以实现我的目标吗?



2
我本来是要建议的diff <(zcat file1) <(zcat file2),但是mrethub的建议zdiff看起来要好得多。
凯文

backuppc为您完成了您要手动实现的任务
drone.ah 2013年

@ drohne.ah backuppc如果每天只有一个文件的话,可能有点过分……(我想这就像是SQL转储,对于gzip来说很有意义)
mreithub

1
@mdpc MD5中的算法问题可能无关紧要。可能会构造冲突,但可能唯一需要关注的是偶然发生的事件,而不是攻击者发生的事件。除非您拥有〜2 ^ 64个文件,否则这仍然不太可能发生。即使是原像攻击也可能无关紧要。
derobert

Answers:


7

您可以使用mreithub zcmpzdiff在其评论中建议使用(或Kevin的命令,类似)。这些文件的效率相对较低,因为它们实际上会解压缩两个文件,然后将它们传递给cmpdiff。如果您只想回答“他们是一样的”,那么cmp它会更快。

使用的md5sum方法非常好,但是运行之前,您需要使用MD5 gzip。然后将其与生成的文件一起存储在.gz文件中。然后,您可以在压缩文件之前轻松地比较文件。如果名称相同,md5sum -c将为您执行此操作。

$ mkdir "backup1"
$ cd backup1
$ echo "test" > backup-file
$ md5sum backup-file > backup-file.md5
$ gzip -9 backup-file

以及下一个备份:

$ mkdir "backup2"
$ cd backup2
$ echo "test" > backup-file
$ md5sum -c ../backup1/backup-file.md5 
backup-file: OK

因此它没有改变。OTOH,是否已更改:

$ echo "different" > backup-file
$ md5sum -c ../backup1/backup-file.md5 
backup-file: FAILED
md5sum: WARNING: 1 computed checksum did NOT match

如果传递--quiet给它,它只会给您退出代码。0表示匹配,非0表示不同。

MD5相当快,但并非如此。MD4(openssl md4我相信,这是您在命令行上获得的最好的),速度大约是它的两倍(它和MD5都不安全,但是当没有人试图颠覆它们时,两者的抗碰撞性都差不多)。SHA-1(sha1sum)更安全,但速度较慢;SHA-256(sha256sum)是安全的,但速度仍然更慢。CRC32应该快很多倍,但是要短一些,因此会有更多的随机冲突。它也完全不安全。


zdiff似乎很浪费,因为我只想知道文件是否已更改,而不是什么zcmp看起来很有趣,我会尝试的。
Lekensteyn

7

@derobert的答案很好,尽管我想分享发现的其他信息。

gzip -l -v

gzip压缩的文件已经包含一个哈希(尽管不安全,请参见此SO post):

$ echo something > foo
$ gzip foo
$ gzip -v -l foo.gz 
method  crc     date  time           compressed        uncompressed  ratio uncompressed_name
defla 18b1f736 Feb  8 22:34                  34                  10 -20.0% foo

可以将CRC和未压缩的大小结合起来以快速获得指纹:

gzip -v -l foo.gz | awk '{print $2, $7}'

cmp

要检查两个字节是否相等,请使用cmp file1 file2。现在,已压缩的文件具有一些标头,并附加了数据和页脚(CRC加原始大小)。所述的gzip格式描述示出了报头包含当文件被压缩和该文件名是在10个字节的报头之后附加了空终止字符串的时间。

因此,假设文件名是恒定的并且使用了相同的命令(gzip "$name"),则可以通过使用cmp和跳过包括时间的第一个字节来检查两个文件是否不同:

cmp -i 8 file1 file2

注意:假设相同的压缩选项很重要,否则该命令将始终将文件报告为不同。发生这种情况是因为压缩选项存储在标头中,并且可能会影响压缩数据。cmp仅查看原始字节,而不将其解释为gzip。

如果文件名具有相同的长度,则可以尝试在读取文件名后计算要跳过的字节。如果文件名大小不同,则可以cmp在跳过字节之后运行,例如cmp <(cut -b9- file1) <(cut -b10- file2)

zcmp

这绝对是最好的方法,它首先压缩数据,然后开始与比较字节cmp(确实,这是在zcmpzdiff)shellscript中完成的操作)。

一个注意事项,请不要害怕手册页中的以下注意事项:

如果在比较之前必须先解压缩两个文件,则将第二个文件解压缩到/ tmp。在所有其他情况下,zdiff和zcmp仅使用管道。

当您有足够新的Bash时,压缩将不会使用临时文件,而只会使用管道。或者,正如zdiff消息人士所说:

# Reject Solaris 8's buggy /bin/bash 2.03.

如果字节4(FLG)为0,则文件名不在标题中,因此您不必担心其长度。另外,我发现gzip -v -l如果标头中的四个MTIME字节为零,则将报告文件时间而不是MTIME。还要注意,如果存在MTIME,通常它比文件时间早一点,因为它是压缩开始的时间。
kitchin '16

0

要比较两个gzip文件,仅比较内容,一个命令,不diff比较,仅比较md5sum

$ diff -q <(zcat one.gz|md5sum|cut -f1 -d' ') \
          <(zcat two.gz|md5sum|cut -f1 -d' ') \
    && echo same || echo not_same

您也可以“过滤”相关差异,

$ diff -q <(zcat one.gz|grep -v '^-- Dump completed'|md5sum|cut -f1 -d' ') \
          <(zcat two.gz|grep -v '^-- Dump completed'|md5sum|cut -f1 -d' ') \
   && echo same || echo not_same

如果要编写脚本,我建议您使用过滤器功能(未经测试,仅作为示例),

do_filter_sum() {
  zcat $1 | grep -v '^-- Dump completed' | md5sum | cut -f1 -d' '
}

diff -q <(do_filter_sum one.gz) \
        <(do_filter_sum two.gz) \
        && echo same || echo not_same

md5sum很浪费,可以使用cmpzcatgrep可以合并为zgrep
Lekensteyn

是的,md5sum不需要进行比较(除非已经生成了它们);自从derobert使用它以来,我就一直使用它。zgrep只是一个脚本,基本上可以执行gunzip和grep(或视情况而定sed),因此两者之间几乎没有区别。发布的脚本有意显示为带有可插拔部件的管道链;将所有内容合并为一个命令有什么乐趣?
迈克尔

1
而且zcat就是gunzip -c。使用正确的工具完成正确的工作,KISS胜过膨胀。在这种情况下,我会花时间写一些可以根据需要生成硬链接的东西,这会更加有趣。
Lekensteyn
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.