Come posso verificare se due file compressi con gzip sono uguali?


11

Sto cercando di risparmiare spazio mentre eseguo un backup "stupido" semplicemente scaricando i dati in un file di testo. Il mio script di backup viene eseguito quotidianamente e assomiglia a questo:

  1. Creare una directory denominata dopo la data di backup.
  2. Scarica alcuni dati in un file di testo "$name".
  3. Se il file è valido, gzip esso: gzip "$name". In caso contrario, rm "$name".

Ora voglio aggiungere un ulteriore passaggio per rimuovere un file se gli stessi dati erano disponibili anche il giorno prima (e creare un collegamento simbolico o hardlink).

All'inizio ho pensato di usare md5sum "$name", ma questo non funziona perché memorizzo anche il nome file e la data di creazione.

Non gziphanno la possibilità di confrontare due file compressi con gzip e ditemi se sono uguali o no? Se gzipnon esiste tale opzione, esiste un altro modo per raggiungere il mio obiettivo?



2
Stavo per suggerire diff <(zcat file1) <(zcat file2), ma il suggerimento di mrethub di zdiffapparire molto meglio.
Kevin,

backuppc fa per te quello che stai cercando di ottenere manualmente
drone.ah

@ drohne.ah backuppc potrebbe essere una specie di overkill se si tratta di un solo file al giorno ... (Immagino sia un dump SQL dove ha molto senso gzip)
mreithub

1
@mdpc I problemi dell'algoritmo in MD5 probabilmente non sono rilevanti. È possibile costruire collisioni, ma probabilmente l'unica preoccupazione sono quelle che accadono per caso, non da un attaccante. E questo è ancora improbabile che accada fino a quando non hai ~ 2 ^ 64 file. Anche un attacco preimage probabilmente non ha importanza.
derobert,

Risposte:


7

Puoi usare zcmpo zdiffcome suggerisce mreithub nel suo commento (o il comando di Kevin, che è simile). Questi saranno relativamente inefficienti, poiché in realtà decomprimono entrambi i file e poi li passano a cmpo diff. Se vuoi solo rispondere "sono gli stessi", vuoi cmp, sarà molto più veloce.

Il tuo approccio con the md5sumè perfettamente buono, ma devi prendere MD5 prima di eseguire gzip. Quindi archiviarlo in un file accanto al .gzfile risultante . È quindi possibile confrontare facilmente il file, prima di comprimerlo. Se il nome è lo stesso, md5sum -clo farà per te.

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

E il prossimo backup:

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

Quindi non è cambiato. OTOH, se fosse cambiato:

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

Se lo passi --quiet, ti darà solo il codice di uscita. 0 per abbinato, non-0 per differenziato.

MD5 è abbastanza veloce, ma non così incredibilmente. MD4 ( openssl md4è il migliore che si ottiene dalla riga di comando, credo) è circa il doppio più veloce (né esso né MD5 è sicuro, ma entrambi sono circa resistenti alle collisioni quando nessuno sta cercando di sovvertirli). SHA-1 ( sha1sum) è più sicuro, ma più lento; SHA-256 ( sha256sum) è sicuro, ma ancora più lento. CRC32 dovrebbe essere molte volte più veloce, ma è più corto e quindi avrà più collisioni casuali. È anche del tutto insicuro.


zdiffsembra uno spreco perché voglio solo sapere se un file è cambiato, non cosa . zcmpsembra interessante, lo proverò.
Lekensteyn,

7

La risposta di @derobert è ottima, anche se voglio condividere alcune altre informazioni che ho trovato.

gzip -l -v

I file compressi con gzip contengono già un hash (non sicuro però, vedi questo 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

È possibile combinare CRC e dimensioni non compresse per ottenere un'impronta digitale veloce:

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

cmp

Per verificare se due byte sono uguali o meno, utilizzare cmp file1 file2. Ora, un file gzipped ha qualche intestazione con i dati e il piè di pagina (CRC più dimensioni originali) aggiunti. La descrizione del formato gzip mostra che l'intestazione contiene l'ora in cui il file è stato compresso e che il nome del file è una stringa con terminazione nulla che viene aggiunta dopo l'intestazione a 10 byte.

Quindi, supponendo che il nome del file sia costante e gzip "$name"venga utilizzato lo stesso comando ( ), si può verificare se due file sono diversi usando cmpe saltando i primi byte incluso il tempo:

cmp -i 8 file1 file2

Nota : il presupposto che le stesse opzioni di compressione siano importanti, altrimenti il ​​comando riporterà sempre il file come diverso. Ciò accade perché le opzioni di compressione sono memorizzate nell'intestazione e possono influire sui dati compressi. cmpguarda solo byte grezzi e non lo interpreta come gzip.

Se hai nomi di file della stessa lunghezza, puoi provare a calcolare i byte da saltare dopo aver letto il nome del file. Quando i nomi dei file sono di dimensioni diverse, è possibile eseguire cmpdopo aver saltato i byte, ad esempio cmp <(cut -b9- file1) <(cut -b10- file2).

zcmp

Questo è sicuramente il modo migliore per andare, comprime prima i dati e inizia a confrontare i byte con cmp(davvero, questo è ciò che viene fatto nello shellscript zcmp( zdiff)).

Una nota, non aver paura della seguente nota nella pagina del manuale:

Quando entrambi i file devono essere decompressi prima del confronto, il secondo è decompresso in / tmp. In tutti gli altri casi, zdiff e zcmp usano solo una pipe.

Quando hai un Bash sufficientemente nuovo, la compressione non utilizzerà un file temporaneo, ma solo una pipe. Oppure, come zdiffdice la fonte:

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

Se il byte 4 (FLG) è 0, il nome del file non è nell'intestazione, quindi non devi preoccuparti della sua lunghezza. Inoltre, ho scoperto che gzip -v -lindicherà il tempo del file anziché MTIME se i quattro byte MTIME nell'intestazione sono zero. Nota anche se MTIME è presente, in genere è un po 'prima dell'ora del file perché è quando è iniziata la compressione.
kitchin

0

Per confrontare due file gzip, solo il contenuto, un comando, no diff, solo il confrontomd5sum

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

Puoi anche "filtrare" per differenze rilevanti,

$ 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

Se lo scripting, consiglierei una funzione di filtro (non testata, solo un esempio),

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

Il md5sum è uno spreco, puoi usarlo cmp. zcate greppuò essere unito in zgrep.
Lekensteyn,

vero, md5sum non è necessario per il confronto (a meno che tu non li abbia già generati); L'ho usato da quando lo ha usato Derobert. zgrep è solo uno script che fondamentalmente fa gunzip e grep (o sed a seconda dei casi), quindi c'è poca differenza lì. la sceneggiatura pubblicata viene intenzionalmente mostrata come una catena di tubi con parti collegabili; qual è il divertimento nel fondere tutto in un unico comando?
michael

1
Ed zcatè giusto gunzip -c. Usa lo strumento giusto per il lavoro giusto, KISS è meglio di gonfiare. In questo caso passerei il mio tempo a scrivere qualcosa che generi collegamenti concreti secondo necessità, è più divertente.
Lekensteyn,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.