Как я могу проверить, равны ли два файла GZIP?

11

Я пытаюсь сэкономить место при выполнении «тупой» резервной копии, просто сбрасывая данные в текстовый файл. Мой резервный скрипт выполняется ежедневно и выглядит так:

  1. Создайте каталог с именем после даты резервного копирования.
  2. Скопируйте некоторые данные в текстовый файл "$name".
  3. Если файл является действительным, GZIP его: gzip "$name". В противном случае rm "$name".

Теперь я хочу добавить дополнительный шаг для удаления файла, если те же данные были также доступны накануне (и создать символическую ссылку или жесткую ссылку).

Сначала я думал об использовании md5sum "$name", но это не работает, потому что я также храню имя файла и дату создания.

Есть ли gzipвозможность сравнить два сжатых файла и сказать мне, равны они или нет? Если gzipнет такой возможности, есть ли другой способ достичь моей цели?

Lekensteyn
источник
1
Попробуйте это: linux.die.net/man/1/zdiff
mreithub
2
Я собирался предложить diff <(zcat file1) <(zcat file2), но предложение mrethub zdiffвыглядит намного лучше.
Кевин
backuppc делает для вас то, что вы пытаетесь достичь вручную
drone.ah
@ drohne.ah backuppc может показаться излишним, если это всего лишь один файл в день ... (я думаю, это похоже на дамп SQL, в котором есть смысл использовать gzip)
mreithub
1
@mdpc Проблемы с алгоритмом в MD5, вероятно, не актуальны. Можно создавать столкновения, но, вероятно, единственная проблема - это случайные, а не атакующие. И это вряд ли произойдет, пока у вас не будет ~ 2 ^ 64 файлов. Даже прообразная атака, вероятно, не имеет значения.
Дероберт

Ответы:

7

Вы можете использовать zcmpили, zdiffкак предлагает mreithub в своем комментарии (или команда Кевина, которая похожа). Они будут относительно неэффективными, поскольку они распаковывают оба файла и затем передают их cmpили diff. Если вы просто хотите ответить «они одинаковые», вы хотите 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

Так что это не изменилось. ОТОХ, если бы это изменилось:

$ 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 должен быть во много раз быстрее, но короче и, следовательно, будет иметь больше случайных коллизий. Это также совершенно небезопасно.

derobert
источник
zdiffкажется пустой тратой, так как я просто хочу знать , изменился ли файл, а не что . zcmpвыглядит интересно, я попробую это.
Лекенштейн
7

Ответ @derobert великолепен, хотя я хочу поделиться другой информацией, которую нашел.

gzip -l -v

Сжатые gzip файлы уже содержат хеш (хотя и небезопасно, см. этот пост ):

$ 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 показывает , что заголовок содержит время , когда файл был сжат и что имя файла является NUL-завершённой строкой , которая добавляется после заголовка 10 байт.

Итак, предполагая, что имя файла является постоянным и gzip "$name"используется одна и та же команда ( ), можно проверить, отличаются ли два файла, используя cmpи пропуская первые байты, включая время:

cmp -i 8 file1 file2

Примечание : предполагается, что важны одинаковые параметры сжатия, в противном случае команда всегда будет сообщать о файле как о другом. Это происходит потому, что параметры сжатия хранятся в заголовке и могут влиять на сжатые данные. cmpпросто смотрит на необработанные байты и не интерпретирует их как gzip.

Если у вас есть имена файлов одинаковой длины, вы можете попытаться вычислить байты, которые будут пропущены после прочтения имени файла. Когда имена файлов имеют разный размер, вы можете запустить cmpпосле пропуска байтов, например cmp <(cut -b9- file1) <(cut -b10- file2).

zcmp

Это определенно лучший способ, сначала он сжимает данные и начинает сравнивать байты с cmp(на самом деле, это то, что делается в zcmp( zdiff) оболочке).

Одно замечание, не бойтесь следующего примечания на странице руководства:

Когда оба файла должны быть распакованы перед сравнением, второй распаковывается в / tmp. Во всех остальных случаях zdiff и zcmp используют только трубу.

Когда у вас достаточно новый Bash, сжатие не будет использовать временный файл, а только канал. Или, как zdiffговорит источник:

# Reject Solaris 8's buggy /bin/bash 2.03.
Lekensteyn
источник
Если байт 4 (FLG) равен 0, то имя файла отсутствует в заголовке, поэтому вам не нужно беспокоиться о его длине. Кроме того, я обнаружил, gzip -v -lчто сообщит время файла вместо MTIME, если четыре байта MTIME в заголовке равны нулю. Также обратите внимание, что если есть MTIME, обычно это немного раньше времени файла, потому что это когда начинается сжатие.
Китчин
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 - это отходы, которые вы можете использовать cmp. zcatи grepмогут быть объединены в zgrep.
Лекенштейн
true, md5sum не нужно сравнивать (если вы их уже не генерировали); Я просто использовал это, так как Дероберт использовал это. zgrep - это всего лишь скрипт, который в основном выполняет gunzip и grep (или sed в зависимости от обстоятельств), поэтому здесь нет никакой разницы. сценарий в том виде, в котором он был опубликован, намеренно показан в виде цепочки труб со вставными деталями; в чем удовольствие объединять все в одну команду?
Майкл
1
И zcatэто просто gunzip -c. Используйте правильный инструмент для правильной работы, KISS лучше, чем раздувать. В этом случае я бы потратил свое время на написание чего-то, что генерирует жесткие ссылки по мере необходимости, это веселее.
Лекенштейн