Como posso verificar se dois arquivos gzipados são iguais?

8

Estou tentando economizar espaço ao fazer um backup "estúpido" simplesmente colocando dados em um arquivo de texto. Meu script de backup é executado diariamente e se parece com isso:

  1. Crie um diretório com o nome da data de backup.
  2. Envie alguns dados para um arquivo de texto "$name" .
  3. Se o arquivo for válido, gzip it: gzip "$name" . Caso contrário, rm "$name" .

Agora, quero adicionar uma etapa adicional para remover um arquivo, se os mesmos dados também estiverem disponíveis no dia anterior (e criar um link simbólico ou um link físico).

Primeiro, pensei em usar md5sum "$name" , mas isso não funciona porque também armazeno o nome do arquivo e a data de criação.

O gzip tem uma opção para comparar dois arquivos compactados com o gzip e me informar se eles são iguais ou não? Se gzip não tiver essa opção, existe outra maneira de atingir meu objetivo?

    
por Lekensteyn 08.02.2013 / 19:40

3 respostas

6
A resposta do @derobert é ótima, embora eu queira compartilhar algumas outras informações que eu encontrei. / p>

gzip -l -v

Os ficheiros comprimidos para gzip já contêm um hash (não é seguro, consulte esta publicação 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

É possível combinar o CRC e o tamanho descompactado para obter uma impressão digital rápida:

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

cmp

Para verificar se dois bytes são iguais ou não, use cmp file1 file2 . Agora, um arquivo gzipado tem algum cabeçalho com os dados e rodapé (CRC mais tamanho original) anexados. A descrição do formato gzip mostra que o cabeçalho contém a hora em que o arquivo foi compactado e que o nome do arquivo é uma string terminada em nul que é anexada após o cabeçalho de 10 bytes.

Assim, assumindo que o nome do arquivo é constante e o mesmo comando ( gzip "$name" ) é usado, pode-se verificar se dois arquivos são diferentes usando cmp e ignorando os primeiros bytes, incluindo o tempo:

cmp -i 8 file1 file2

Nota : a suposição de que as mesmas opções de compactação são importantes, caso contrário, o comando sempre reportará o arquivo como diferente. Isso acontece porque as opções de compactação são armazenadas no cabeçalho e podem afetar os dados compactados. cmp apenas analisa os bytes brutos e não os interpreta como gzip.

Se você tem nomes de arquivos do mesmo tamanho, então você poderia tentar calcular os bytes a serem pulados depois de ler o nome do arquivo. Quando os nomes dos arquivos são de tamanhos diferentes, você pode executar cmp depois de ignorar bytes, como cmp <(cut -b9- file1) <(cut -b10- file2) .

zcmp

Este é definitivamente o melhor caminho a seguir, ele primeiro comprime os dados e começa a comparar os bytes com cmp (na verdade, isso é o que é feito no shell zcmp ( zdiff )).

Uma nota, não tenha medo da seguinte nota na página do manual:

When both files must be uncompressed before comparison, the second is uncompressed to /tmp. In all other cases, zdiff and zcmp use only a pipe.

Quando você tem um Bash suficientemente novo, a compactação não usará um arquivo temporário, apenas um pipe. Ou, como a fonte zdiff diz:

# Reject Solaris 8's buggy /bin/bash 2.03.
    
por 08.02.2013 / 23:02
6

Você pode usar zcmp ou zdiff como mreithub sugere em seu comentário (ou o comando de Kevin, que é similar). Eles serão relativamente ineficientes, já que, na verdade, descompactam os arquivos e depois os transferem para cmp ou diff . Se você quer apenas responder "eles são os mesmos", você quer cmp , vai ser muito mais rápido.

Sua abordagem com o md5sum é perfeitamente boa, mas você precisa levar o MD5 antes executando gzip . Em seguida, armazene-o em um arquivo ao lado do arquivo .gz resultante. Você pode comparar o arquivo facilmente, antes de compactá-lo. Se o nome for o mesmo, md5sum -c fará isso por você.

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

E o próximo backup:

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

Então não mudou. OTOH, mudou:

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

Se você passar --quiet para ele, ele fornecerá o código de saída. 0 para correspondência, não 0 para diferente.

O MD5 é bastante rápido, mas não é tão impressionante. O MD4 ( openssl md4 é o melhor que você obtém na linha de comando, acredito) é cerca de duas vezes mais rápido (nem ele nem o MD5 é seguro, mas ambos são resistentes à colisão quando ninguém está tentando subvertê-los). SHA-1 ( sha1sum ) é mais seguro, mas mais lento; SHA-256 ( sha256sum ) é seguro, mas ainda mais lento. O CRC32 deve ser muitas vezes mais rápido, mas é mais curto e, portanto, terá mais colisões aleatórias. É também totalmente inseguro.

    
por 08.02.2013 / 20:14
0

Para comparar dois arquivos gzip, apenas o conteúdo, um comando, sem diff , apenas comparando md5sum

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

Você também pode "filtrar" por diferenças relevantes,

$ 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 o script, eu recomendo uma função de filtro (não testado, apenas um exemplo),

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
    
por 14.03.2013 / 13:17