¿Cómo puedo verificar si dos archivos comprimidos son iguales?

11

Estoy tratando de ahorrar espacio mientras hago una copia de seguridad "tonta" simplemente volcando datos en un archivo de texto. Mi script de respaldo se ejecuta a diario y tiene este aspecto:

  1. Cree un directorio con el nombre de la fecha de la copia de seguridad.
  2. Volcar algunos datos en un archivo de texto "$name".
  3. Si el archivo es válido, gzip: gzip "$name". De lo contrario, rm "$name".

Ahora quiero agregar un paso adicional para eliminar un archivo si los mismos datos también estaban disponibles el día anterior (y crear un enlace simbólico o un enlace duro).

Al principio pensé en usar md5sum "$name", pero esto no funciona porque también almaceno el nombre de archivo y la fecha de creación.

¿ gzipTiene una opción para comparar dos archivos comprimidos y decirme si son iguales o no? Si gzipno tiene esa opción, ¿hay otra forma de lograr mi objetivo?

Lekensteyn
fuente
1
Pruebe esto: linux.die.net/man/1/zdiff
mreithub
2
Iba a sugerir diff <(zcat file1) <(zcat file2), pero la sugerencia de mrethub de zdiffparece mucho mejor.
Kevin
backuppc hace por usted lo que está tratando de lograr manualmente
drone.ah
@ drohne.ah backuppc podría ser una exageración si es solo un archivo por día ... (Supongo que es como un volcado de SQL donde tiene mucho sentido gzip)
mreithub
1
@mdpc Los problemas de algoritmo en MD5 probablemente no son relevantes. Es posible construir colisiones, pero probablemente la única preocupación son las que ocurren por casualidad, no por un atacante. Y eso es poco probable que suceda hasta que tenga ~ 2 ^ 64 archivos. Incluso un ataque previo a la imagen probablemente no importe.
derobert

Respuestas:

7

Puede usar zcmpo zdiffcomo sugiere mreithub en su comentario (o el comando de Kevin, que es similar). Estos serán relativamente ineficiente, ya que en realidad descomprimir los dos archivos y luego hacerlos pasar a cmpo diff. Si solo quiere responder "son lo mismo", lo desea cmp, será mucho más rápido.

Su enfoque con el md5sumes perfectamente bueno, pero debe tomar el MD5 antes de ejecutarlo gzip. Luego guárdelo en un archivo junto con el .gzarchivo resultante . Luego puede comparar el archivo fácilmente, antes de comprimirlo. Si el nombre es el mismo, md5sum -clo hará por usted.

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

Y la próxima copia de seguridad:

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

Entonces no ha cambiado. OTOH, si hubiera cambiado:

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

Si lo pasa --quiet, solo le dará el código de salida. 0 para emparejado, no 0 para diferido.

MD5 es bastante rápido, pero no tan llamativo. MD4 ( openssl md4es lo mejor que obtienes en la línea de comando, creo) es aproximadamente dos veces más rápido (ni él ni MD5 son seguros, pero ambos son tan resistentes a colisiones cuando nadie intenta subvertirlos). SHA-1 ( sha1sum) es más seguro, pero más lento; SHA-256 ( sha256sum) es seguro, pero aún más lento. CRC32 debería ser muchas veces más rápido, pero es más corto y, por lo tanto, tendrá más colisiones aleatorias. También es completamente inseguro.

derobert
fuente
zdiffparece un desperdicio ya que solo quiero saber si un archivo ha cambiado, no qué . zcmpParece interesante, lo intentaré.
Lekensteyn
7

La respuesta de @derobert es excelente, aunque quiero compartir alguna otra información que he encontrado.

gzip -l -v

Los archivos comprimidos con gzip ya contienen un hash (aunque no es seguro, vea esta publicación 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

Se puede combinar el CRC y el tamaño sin comprimir para obtener una huella digital rápida:

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

cmp

Para verificar si dos bytes son iguales o no, use cmp file1 file2. Ahora, un archivo comprimido tiene algún encabezado con los datos y el pie de página (CRC más el tamaño original) adjuntos. La descripción del formato gzip muestra que el encabezado contiene la hora en que se comprimió el archivo y que el nombre del archivo es una cadena terminada en nulo que se agrega después del encabezado de 10 bytes.

Entonces, suponiendo que el nombre del archivo sea constante y gzip "$name"se use el mismo comando ( ), se puede verificar si dos archivos son diferentes usando cmpy omitiendo los primeros bytes, incluida la hora:

cmp -i 8 file1 file2

Nota : la suposición de que las mismas opciones de compresión es importante, de lo contrario, el comando siempre informará que el archivo es diferente. Esto sucede porque las opciones de compresión se almacenan en el encabezado y pueden afectar los datos comprimidos. cmpsolo mira los bytes sin procesar y no lo interpreta como gzip.

Si tiene nombres de archivo de la misma longitud, puede intentar calcular los bytes que se omitirán después de leer el nombre de archivo. Cuando los nombres de archivo son de diferente tamaño, puede ejecutar cmpdespués de omitir bytes, como cmp <(cut -b9- file1) <(cut -b10- file2).

zcmp

Esta es definitivamente la mejor manera de hacerlo, primero comprime los datos y comienza a comparar los bytes con cmp(realmente, esto es lo que se hace en el zcmp( zdiff) shellscript).

Una nota, no tenga miedo de la siguiente nota en la página del manual:

Cuando ambos archivos deben descomprimirse antes de la comparación, el segundo se descomprime en / tmp. En todos los demás casos, zdiff y zcmp usan solo una tubería.

Cuando tiene un Bash suficientemente nuevo, la compresión no utilizará un archivo temporal, solo una tubería. O, como zdiffdice la fuente:

# Reject Solaris 8's buggy /bin/bash 2.03.
Lekensteyn
fuente
Si el byte 4 (FLG) es 0, el nombre del archivo no está en el encabezado, por lo que no debe preocuparse por su longitud. Además, descubrí gzip -v -lque informará el tiempo del archivo en lugar de MTIME si los cuatro bytes MTIME en el encabezado son cero. También tenga en cuenta que si MTIME está allí, generalmente es un poco antes del tiempo del archivo porque es cuando comenzó la compresión.
kitchin
0

Para comparar dos archivos gzip, solo el contenido, un comando, no diff, solo compararmd5sum

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

También puede "filtrar" las diferencias 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

Si se trata de secuencias de comandos, recomendaría una función de filtro (no probada, solo un ejemplo),

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
Miguel
fuente
El md5sum es un desperdicio, puedes usarlo cmp. zcaty grepse pueden combinar en zgrep.
Lekensteyn
Es cierto, md5sum no es necesario comparar (a menos que ya los haya generado); Lo acabo de usar desde que lo usó Derobert. zgrep es solo un script que básicamente hace gunzip y grep (o sed según sea el caso), por lo que hay poca diferencia allí. la secuencia de comandos publicada se muestra intencionalmente como una cadena de tuberías con partes conectables; ¿Cuál es la diversión de fusionar todo en un solo comando?
Michael
1
Y zcates justo gunzip -c. Use la herramienta adecuada para el trabajo correcto, KISS es mejor que la hinchazón. En este caso, pasaría mi tiempo escribiendo algo que genere enlaces duros según sea necesario, eso es más divertido.
Lekensteyn