Como posso verificar se dois arquivos compactados em gz são iguais?

11

Eu estou tentando economizar espaço ao fazer um backup "burro" simplesmente despejando dados em um arquivo de texto. Meu script de backup é executado diariamente e fica assim:

  1. Crie um diretório com o nome da data do backup.
  2. Despejar alguns dados em um arquivo de texto "$name".
  3. Se o arquivo for válido, gzip-lo: 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 link simbólico ou link físico).

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

Tem gzipuma opção para comparar dois arquivos compactados em gzip e me dizer se são iguais ou não? Se gzipnão houver essa opção, existe outra maneira de alcançar meu objetivo?

Lekensteyn
fonte
1
Tente isto: linux.die.net/man/1/zdiff
mreithub
2
Eu ia sugerir diff <(zcat file1) <(zcat file2), mas a sugestão de mrethub zdiffparece muito melhor.
Kevin
backuppc faz para você o que você está tentando alcançar manualmente
drone.ah
@ drohne.ah backuppc poderia ser uma espécie de exagero se é apenas um arquivo por dia ... (eu acho que é sth como um despejo SQL onde faz muito sentido para gzip)
mreithub
1
@mdpc Os problemas de algoritmo no MD5 provavelmente não são relevantes. É possível construir colisões, mas provavelmente a única preocupação são as que acontecem por acaso, não por um invasor. E ainda é improvável que isso aconteça até que você tenha ~ 2 ^ 64 arquivos. Mesmo um ataque de pré-imagem provavelmente não importa.
8263 derobert

Respostas:

7

Você pode usar zcmpou zdiffcomo o mreithub sugere em seu comentário (ou no comando de Kevin, que é semelhante). Eles serão relativamente ineficientes, pois na verdade descompactam os dois arquivos e os passam para cmpou diff. Se você quer apenas responder "eles são iguais", você quer cmp, será muito mais rápido.

Sua abordagem com o md5sumé perfeitamente boa, mas você precisa usar o MD5 antes de executar gzip. Em seguida, armazene-o em um arquivo ao lado do .gzarquivo resultante . Você pode comparar o arquivo facilmente, antes de compactá-lo. Se o nome for o mesmo, md5sum -cfará 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, ele 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 --quietpara ele, ele fornecerá o código de saída. 0 para correspondência, não 0 para diferença.

O MD5 é bastante rápido, mas não de forma flagrante. O MD4 ( openssl md4é o melhor que você obtém na linha de comando, acredito) é duas vezes mais rápido (nem o MD5 é seguro, mas ambos são tão resistentes a colisões quando ninguém tenta subvertê-los). SHA-1 ( sha1sum) é mais seguro, mas mais lento; O 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.

derobert
fonte
zdiffparece um desperdício, pois eu só quero saber se um arquivo foi alterado, não o que . zcmpparece interessante, vou tentar isso.
precisa saber é o seguinte
7

A resposta de @derobert é ótima, embora eu queira compartilhar outras informações que encontrei.

gzip -l -v

Os arquivos compactados com gzip já contêm um hash (embora não seja seguro, veja este post no 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

Pode-se combinar o CRC e o tamanho não compactado 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 compactado com gzip tem algum cabeçalho com os dados e o rodapé (CRC mais o 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 sequência terminada em nulo que é anexada após o cabeçalho de 10 bytes.

Portanto, assumindo que o nome do arquivo seja constante e o mesmo comando ( gzip "$name") seja usado, é possível verificar se dois arquivos são diferentes usando cmpe pulando os primeiros bytes, incluindo o horário:

cmp -i 8 file1 file2

Nota : supondo que as mesmas opções de compactação sejam 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. cmpapenas analisa os bytes brutos e não os interpreta como gzip.

Se você tiver nomes de arquivos do mesmo tamanho, tente calcular os bytes a serem ignorados depois de ler o nome do arquivo. Quando os nomes de arquivos tiverem tamanhos diferentes, você poderá executar cmpdepois de pular 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(realmente, é isso que é feito no shellscript zcmp( zdiff)).

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

Quando os dois arquivos devem ser descompactados antes da comparação, o segundo é descompactado para / tmp. Nos demais casos, o zdiff e o zcmp usam apenas um pipe.

Quando você tem um Bash suficientemente novo, a compactação não usa um arquivo temporário, apenas um canal. Ou, como a zdifffonte diz:

# Reject Solaris 8's buggy /bin/bash 2.03.
Lekensteyn
fonte
Se o byte 4 (FLG) for 0, o nome do arquivo não estará no cabeçalho; portanto, você não precisa se preocupar com o tamanho. Além disso, eu descobri gzip -v -lque relatará o tempo do arquivo em vez de MTIME se os quatro bytes MTIME no cabeçalho forem zero. Observe também que se MTIME estiver lá, normalmente é um pouco antes do horário do arquivo, porque é quando a compactação é iniciada.
Kitchin
0

Para comparar dois arquivos gzip, apenas o conteúdo, um comando, não diff, apenas comparandomd5sum

$ 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" as 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 estiver usando scripts, recomendo uma função de filtro (não testada, 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
Michael
fonte
O md5sum é um desperdício, você pode usar cmp. zcate greppode ser mesclado zgrep.
Lekensteyn
verdade, o md5sum não é necessário para comparar (a menos que você já os tenha gerado); Eu apenas usei desde que derobert usou. O zgrep é apenas um script que basicamente executa o gunzip e o grep (ou sed, conforme o caso), então há pouca diferença lá. o script postado é intencionalmente mostrado como uma cadeia de tubos com peças conectáveis; qual é a graça de mesclar tudo em um único comando?
michael
1
E zcaté justo gunzip -c. Use a ferramenta certa para o trabalho certo, o KISS é melhor do que inchar. Nesse caso, eu gastaria meu tempo escrevendo algo que gera links físicos, conforme necessário, que é mais divertido.
Lekensteyn