Jak mogę sprawdzić, czy dwa pliki spakowane gzip są równe?

11

Próbuję zaoszczędzić miejsce podczas wykonywania „głupiej” kopii zapasowej, po prostu zrzucając dane do pliku tekstowego. Mój skrypt kopii zapasowej jest wykonywany codziennie i wygląda następująco:

  1. Utwórz katalog o nazwie od daty kopii zapasowej.
  2. Zrzuć niektóre dane do pliku tekstowego "$name".
  3. Jeśli plik jest poprawny, gzip go: gzip "$name". W przeciwnym razie rm "$name".

Teraz chcę dodać dodatkowy krok, aby usunąć plik, jeśli te same dane były również dostępne poprzedniego dnia (i utworzyć dowiązanie symboliczne lub dowiązanie twarde).

Na początku myślałem o użyciu md5sum "$name", ale to nie działa, ponieważ przechowuję również nazwę pliku i datę utworzenia.

Czy gzipistnieje opcja porównania dwóch plików spakowanych gzip i powiedz mi, czy są równe, czy nie? Jeśli gzipnie ma takiej opcji, czy istnieje inny sposób na osiągnięcie mojego celu?

Lekensteyn
źródło
1
Spróbuj tego: linux.die.net/man/1/zdiff
mreithub
2
Chciałem zasugerować diff <(zcat file1) <(zcat file2), ale sugestia mrethuba zdiffwygląda znacznie lepiej.
Kevin
backuppc robi dla ciebie to, co próbujesz osiągnąć ręcznie
drone.ah
@ drohne.ah backuppc może być rodzajem przesady, jeśli jest to tylko jeden plik dziennie ... (Myślę, że jest to coś jak zrzut SQL, w którym gzip ma sens)
mreithub
1
@mdpc Problemy z algorytmem w MD5 prawdopodobnie nie są istotne. Możliwe jest konstruowanie kolizji, ale prawdopodobnie jedynym problemem są te, które zdarzają się przypadkowo, a nie przez atakującego. I to wciąż mało prawdopodobne, dopóki nie będziesz mieć ~ 2 ^ 64 plików. Nawet atak preimage prawdopodobnie nie ma znaczenia.
derobert

Odpowiedzi:

7

Możesz użyć zcmplub, zdiffjak sugeruje mreithub w swoim komentarzu (lub polecenie Kevina, które jest podobne). Będą one stosunkowo nieefektywne, ponieważ faktycznie rozpakowują oba pliki, a następnie przekazują je do cmplub diff. Jeśli chcesz tylko odpowiedzieć „czy oni są tacy sami”, chcesz cmp, będzie znacznie szybciej.

Twoje podejście do md5sumjest bardzo dobre, ale musisz wziąć MD5 przed uruchomieniem gzip. Następnie zapisz go w pliku obok .gzpliku wynikowego . Następnie możesz łatwo porównać plik przed jego skompresowaniem. Jeśli nazwa jest taka sama, md5sum -czrób to dla ciebie.

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

I następna kopia zapasowa:

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

Więc to się nie zmieniło. OTOH, czy to się zmieniło:

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

Jeśli przejdziesz --quietdo niego, otrzymasz kod wyjścia. 0 dla dopasowanych, nie-0 dla różnych.

MD5 jest dość szybki, ale nie tak niesamowicie. MD4 ( openssl md4wydaje mi się, że najlepszy w wierszu poleceń, jest około dwa razy szybszy (ani on, ani MD5 nie są bezpieczne, ale oba są tak samo odporne na kolizje, gdy nikt nie próbuje ich obalić). SHA-1 ( sha1sum) jest bezpieczniejszy, ale wolniejszy; SHA-256 ( sha256sum) jest bezpieczny, ale jeszcze wolniejszy. CRC32 powinien być wielokrotnie szybszy, ale krótszy, a zatem będzie miał więcej losowych kolizji. Jest to również całkowicie niepewne.

derobert
źródło
zdiffwydaje się marnotrawstwem, ponieważ chcę tylko wiedzieć, czy plik się zmienił, a nie co . zcmpwygląda interesująco, spróbuję tego.
Lekensteyn
7

Odpowiedź @derobert jest świetna, choć chcę podzielić się innymi informacjami, które znalazłem.

gzip -l -v

Pliki skompresowane gzip zawierają już skrót (choć nie jest to bezpieczne, zobacz ten post SO ):

$ 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

Można połączyć CRC i nieskompresowany rozmiar, aby uzyskać szybki odcisk palca:

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

cmp

Aby sprawdzić, czy dwa bajty są równe, czy nie, użyj cmp file1 file2. Teraz plik spakowany gzip ma nagłówek z dołączonymi danymi i stopką (CRC plus rozmiar oryginalny). Opis formatu gzip pokazuje, że nagłówek zawiera czas, kiedy plik został skompresowany i że nazwa pliku jest ciągiem nul zakończone, który jest dołączany po nagłówku 10 bajtów.

Zakładając, że nazwa pliku jest stała i gzip "$name"używana jest ta sama komenda ( ), można sprawdzić, czy dwa pliki są różne, używając cmpi pomijając pierwsze bajty, w tym czas:

cmp -i 8 file1 file2

Uwaga : założenie, że te same opcje kompresji są ważne, w przeciwnym razie polecenie zawsze zgłosi plik jako inny. Dzieje się tak, ponieważ opcje kompresji są przechowywane w nagłówku i mogą wpływać na skompresowane dane. cmppo prostu patrzy na surowe bajty i nie interpretuje go jako gzip.

Jeśli masz nazwy plików o tej samej długości, możesz spróbować obliczyć bajty, które zostaną pominięte po odczytaniu nazwy pliku. Gdy nazwy plików mają inny rozmiar, możesz uruchomić cmppo pominięciu bajtów, np cmp <(cut -b9- file1) <(cut -b10- file2).

zcmp

Jest to zdecydowanie najlepsza droga, najpierw kompresuje dane i zaczyna porównywać bajty cmp(tak naprawdę dzieje się to w skoroszycie zcmp( zdiff)).

Jedna uwaga, nie bój się następującej uwagi na stronie podręcznika:

Gdy oba pliki muszą zostać zdekompresowane przed porównaniem, drugi jest nieskompresowany do / tmp. We wszystkich innych przypadkach zdiff i zcmp używają tylko potoku.

Gdy masz wystarczająco nową wersję Bash, kompresja nie użyje pliku tymczasowego, tylko potok. Lub, jak podaje zdiffźródło:

# Reject Solaris 8's buggy /bin/bash 2.03.
Lekensteyn
źródło
Jeśli bajt 4 (FLG) ma wartość 0, nazwa pliku nie znajduje się w nagłówku, więc nie musisz się martwić o jego długość. Znalazłem również, że gzip -v -lzgłosi czas pliku zamiast MTIME, jeśli cztery bajty MTIME w nagłówku są równe zero. Zauważ też, że jeśli jest tam MTIME, zwykle trwa to nieco przed czasem pliku, ponieważ to właśnie wtedy rozpoczęła się kompresja.
kitchin
0

Aby porównać dwa pliki gzip, tylko zawartość, jedno polecenie, nie diff, po prostu porównaniemd5sum

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

Możesz także „filtrować” pod kątem istotnych różnic,

$ 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

W przypadku skryptów poleciłbym funkcję filtru (nie przetestowano, tylko przykład),

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
Michał
źródło
Md5sum to marnotrawstwo, z którego możesz skorzystać cmp. zcati grepmożna je połączyć zgrep.
Lekensteyn
prawda, md5sum nie jest konieczne do porównania (chyba że już je wygenerowałeś); Właśnie go użyłem, odkąd derobert go użył. zgrep to tylko skrypt, który w zasadzie robi gunzip i grep (lub sed, w zależności od przypadku), więc nie ma w tym żadnej różnicy. opublikowany skrypt jest celowo pokazany jako łańcuch rur z częściami wtykowymi; jaka jest zabawa w łączenie wszystkiego w jedno polecenie?
Michał
1
I zcatjest po prostu gunzip -c. Użyj odpowiedniego narzędzia do właściwej pracy, KISS jest lepszy niż wzdęcia. W takim przypadku spędziłbym czas na pisaniu czegoś, co w razie potrzeby generuje twarde linki, co sprawia więcej radości.
Lekensteyn