Diferencia de dos archivos binarios grandes sin procesar similares

Digamos que tengo un archivo de 4 GB abc en mi computadora local. Lo he subido a un servidor distante a través de SFTP, tomó algunas horas.

Ahora he modificado ligeramente el archivo (probablemente 50 MB como máximo, pero no bytes consecutivos en este archivo) localmente, y lo guardé en abc2. También guardé el archivo original abc en mi computadora local.

¿Cómo calcular una diferencia binaria de abc y abc2?

Aplicaciones:

  • Solo pude enviar un archivo patch (probablemente un máximo de 100 MB) a el servidor distante, en lugar de volver a cargar el archivo abc2 completo (¡volvería a tardar unas horas!), y vuelva a crear abc2 en el servidor de abc y patch únicamente.

  • Localmente, en lugar de desperdiciar 8 GB para hacer copias de seguridad de abc y abc2, solo pude ahorrar abc + patch, por lo que se necesitarían < 4100 MB solamente.

¿Cómo hacer esto?

PD: para texto, sé diff, pero aquí estoy buscando algo que podría funcionar para cualquier formato binario sin procesar, podrían ser archivos zip o ejecutables o incluso otros tipos de archivos.

PS2: Si es posible, no quiero usar rsync; Sé que puede replicar cambios entre 2 computadoras de una manera eficiente (sin reenviar datos que no han cambiado), pero aquí realmente quiero tener un archivo patch, que se pueda reproducir más adelante si Tengo abc y patch.

Respuesta

Para la segunda aplicación / problema, usaría un programa de respaldo de deduplicación como restic o borgbackup, en lugar de intentar para realizar un seguimiento manual de los «parches» o diferencias. El programa de respaldo restic le permite respaldar directorios de varias máquinas en el mismo repositorio de respaldo, deduplicando los datos de respaldo tanto entre fragmentos de archivos de una máquina individual como entre máquinas. (No tengo experiencia de usuario con borgbackup, así que no puedo decir nada sobre ese programa).

Calculando y almacenando una diferencia de abc y abc2 se pueden hacer con rsync.

Este es un ejemplo con abc y abc2 siendo 153 MB. El archivo abc2 se modificó sobrescribiendo el primeros 2,3 MB del archivo con algunos otros datos:

 $ ls -lh total 626208 -rw-r--r-- 1 kk wheel 153M Feb 3 16:55 abc -rw-r--r-- 1 kk wheel 153M Feb 3 17:02 abc2  

Creamos parche para transformar abc en abc2 y llamarlo abc-diff:

 $ rsync --only-write-batch=abc-diff abc2 abc  
 $ ls -lh total 631026 -rw-r--r-- 1 kk wheel 153M Feb 3 16:55 abc -rw------- 1 kk wheel 2.3M Feb 3 17:03 abc-diff -rwx------ 1 kk wheel 38B Feb 3 17:03 abc-diff.sh -rw-r--r-- 1 kk wheel 153M Feb 3 17:02 abc2  

El archivo generado abc-diff es el diff real (su «archivo de parche»), mientras que abc-diff.sh esun breve script de shell que rsync crea para usted:

 $ cat abc-diff.sh rsync --read-batch=abc-diff ${1:-abc}  

Este script modifica abc para que sea idéntico a abc2, dado el archivo abc-diff:

 $ md5sum abc abc2 be00efe0a7a7d3b793e70e466cbc53c6 abc 3decbde2d3a87f3d954ccee9d60f249b abc2 $ sh abc-diff.sh $ md5sum abc abc2 3decbde2d3a87f3d954ccee9d60f249b abc 3decbde2d3a87f3d954ccee9d60f249b abc2  

El archivo abc-diff ahora se puede transferir a cualquier otro lugar que tenga abc. Con el comando rsync --read-batch=abc-diff abc, aplicaría el parche al archivo abc, transformando su contenido para que sea el mismo que el abc2 archivo en el sistema donde creó la diferencia.

Volver a aplicar el parche por segunda vez parece seguro. No hay mensajes de error ni cambia el contenido del archivo (la suma de comprobación MD5 no cambia).

Tenga en cuenta que, a menos que cree un «parche inverso» explícito, no hay forma de deshacer fácilmente la aplicación del parche.


También probé escribir la modificación de 2,3 MB en algún otro lugar de los datos abc2, un poco más adelante (a unos 50 MB), así como al principio. El «parche» generado tenía un tamaño de 4,6 MB, lo que sugiere que solo los bits modificados se almacenaron en el parche.

Comentarios

  • Muchas gracias @Kusalananda, ‘ es genial. PD: rsync --read-batch=abc-diff ${1:-abc} (script .sh generado automáticamente) dio remote destination is not allowed with --read-batch rsync error: syntax or usage error (code 1) at main.c(1326) [Receiver=3.1.2], pero rsync --read-batch=abc-diff abc funcionó con éxito.¿Cuál es la diferencia entre estos dos comandos similares?
  • 2/2 ¿Hay alguna manera de tomar abc como entrada, aplicar el parche diff-abc con --read-batch pero sin modificar abc » in situ «, sino que se envía a un nuevo archivo abc3? (si es posible, todo con rsync, sin canalización, para que funcione fácilmente en Linux y Windows, que también tiene rsync.exe disponible)
  • @Basj Los comandos harían cosas diferentes si $1 tuviera un valor. ${1:-abc} significa » usar el primer parámetro posicional ($1) a menos que ‘ s vacío o indefinido. En el caso de que ‘ esté vacío o no esté definido, utilice abc en lugar de «. Yo ‘ asumo que $1 tenía un valor cuando lo probaste, posiblemente algo que interpretó como un dirección de destino remoto.
  • @Basj I ‘ No estoy del todo seguro de que esto sea posible, pero ‘ ll Eche un vistazo mañana después de dormir.
  • Gracias por su respuesta sobre ${1:-abc}. Probablemente falló porque lo probé en Windows (‘ estoy usando rsync tanto en Linux para mi servidor distante como en Windows localmente). Pero ‘ es perfecto ya que rsync --read-batch=abc-diff abc funciona 🙂

Respuesta

¿Cómo calcular una diferencia binaria de abc y abc2?

Usando bsdiff / bspatch o xdelta y otros.

$ bsdiff older newer patch.bin # patch.bin is created [...] $ bspatch older newer patch.bin # newer is created 

Sin embargo, se deben tener en cuenta estas advertencias de las páginas de manual:

  • bsdiff usa una memoria equivalente a 17 veces el tamaño de archivo antiguo , y requiere un tamaño de conjunto de trabajo mínimo absoluto de 8 veces el tamaño de archivo antiguo .
  • bspatch usa una memoria igual al tamaño de archivo antiguo más el tamaño de archivo nuevo , pero puede tolerar un conjunto de trabajo muy pequeño sin una pérdida dramática de rendimiento.

Comentarios

  • ¿Podría mostrar un ejemplo?
  • Gracias por su respuesta. bsdiff uses memory equal to 17 times the size of oldfile así que esto no ‘ normalmente funciona para archivos de 4GB (al menos en mi máquina de 8GB RAM).
  • @Basj Lo que es posible es dividir el archivo de 4 GB en archivos más pequeños (digamos 128 MB cada uno) y hacer deltas individuales. Esto podría incluirse en un guión. chopped-bsdiff: corta los archivos, haz bsdiffs por pares, ponlos en un archivo. chopped-bspatch: leer parches por pares del archivo, aplicar a fragmentos del archivo de entrada, clasificar la salida.
  • @Kaz Ya veo, pero ‘ m más buscando una herramienta lista para usar que se puede llamar en 1 línea (mydiff abc abc2 > patchfile y mypatch abc patchfile > abc3) independientemente del tamaño. Además, si corto en trozos de 128 MB, ¿qué sucede si el primer 1 GB de abc == el último (final) 1 GB de abc2 ? Cuando ‘ comparemos abc-first128mb con abc2-first128mb, no se encontrará ninguna coincidencia, por lo que podría no ser eficiente?

Responder

¿Ha intentado forzar diff para tratar los archivos como texto:

diff -ua abc abc2 

Como se explica aquí .

  • -u salida NUM (por defecto 3) líneas de contexto unificado
  • -a tratar todos los archivos como texto

Esto debería proporcionarle un parche. La desventaja de esto es que las «líneas» pueden ser bastante largas y eso podría inflar el parche.

Comentarios

  • Vaya, sí, no ‘ t realmente quiero el n. Estoy ‘ interesado en saber si funciona porque ‘ no estoy seguro de cuánto tiempo » lines » será.
  • ¡Gracias por tu comentario! Creé dos archivos abc y abc2 muy similares de 256 MB. Luego probé diff -ua abc abc2 > patch, luego copié abc a abc3 e intenté recuperar abc2 gracias a abc3 y patch: patch abc3 < patch, pero no funcionó: al final abc3 era solo 1 KB en lugar de 256 MB. ¿Alguna idea?
  • Hmmm, no estoy seguro de lo que pasó. Simplemente lo hice en mi máquina y funcionó mejor de lo que esperaba.Tomé un archivo de 382M que contenía enteros aleatorios escritos en binario en un archivo. Cambié 3 bytes en él e hice la diferencia y el parche y funcionó. Los archivos resultantes eran md5sum iguales.
  • Si un archivo grande no tiene byte 0x0a, es decir, nueva línea, o muy pocos, sospecho que no lo haría ‘ No funciona tan bien, sería interesante probarlo.
  • Oh, seguro. Puede hacer una suposición fundamentada sobre un binario con wc -l que buscará saltos de línea y, en mi experiencia, se ejecuta muy rápidamente. Esperaría que en un binario arbitrario funcione bastante bien. Por ejemplo, en mi máquina encontré un mp4 de 252M que tenía 1.2 millones de » líneas «, y un que tenía alrededor de 230k, por lo que las » líneas » promedio de menos de 220 bytes y 258 bytes respectivamente. No ‘ no veo por qué estos archivos serían tan diferentes a los demás, pero definitivamente podrías tener mala suerte. En la práctica, sospecho que funcionaría bastante bien y si no ‘ sigue siendo un truco divertido.

Responder

Use xdelta , fue creado exactamente para este tipo de usos. Basado en VCDIFF (RFC 3284) en las últimas versiones.

Comentarios

  • El enlace no funciona (¿hay otra URL?). También podría agregar un ejemplo en unas pocas líneas para mostrar cómo: 1) calcular el archivo diff patch y 2) restaurar abc2 , dado solo abc y patch?
  • Lo sentimos, URL fija
  • Gracias @vonbrand . ¿Tendría un ejemplo así?

Respuesta

Complementos de otras respuestas de acuerdo con mis pruebas:

Con diff

Creé dos archivos abc y . Luego, creemos el archivo diff:

diff -ua abc abc2 > abc-abc2.diff 

Ahora intentemos recuperar abc2 gracias al archivo abc original y abc-abc2.diff:

cp abc abc3 patch abc3 < abc-abc2.diff 

o

cp abc abc3 patch abc3 -i abc-abc2.diff 

o

patch abc -i abc-abc2.diff -o abc3 

Funciona en Linux. También probé en Windows (patch.exe y diff.exe también están disponibles), pero por una razón desconocida falló: el archivo abc3 producido es de solo 1 KB en lugar de 256 MB (I » Actualizaré esta respuesta más adelante aquí).

Con rsync

Como se detalla en la respuesta aceptada, esto funciona:

rsync --only-write-batch=abc-abc2-diff abc2 abc cp abc abc3 rsync --read-batch=abc-abc2-diff abc3 

Con rdiff

Como se detalla en esto answer , esta también es una solución:

rdiff signature abc abc-signature rdiff delta abc-signature abc2 abc-abc2-delta rdiff patch abc abc-abc2-delta abc3 

Probado también en Windows con rdiff.exe de aquí y funciona.

Comentarios

  • Yo ‘ supongo que el parche falló en Windows porque estaba leyendo el archivo de entrada en el modo » text » que indica el final del archivo cuando encuentra un CONTROL -Z (byte 0x18) en el archivo de entrada. Este es un modo heredado de los primeros días de DOS cuando el directorio no registraba la longitud de el archivo y, por tanto, la longitud del archivo se calculó en función del número de sectores de 512 bytes. Si puede decirle a patch que abra el archivo en modo binario, no debería ‘ tener este error.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *