2012-01-24 9 views
6

He escrito un script bash en Cygwin, que es bastante similar al rsync, aunque lo suficientemente diferente como para creer que no puedo usar rsync para lo que necesito. Repite más de mil pares de archivos en los directorios correspondientes, y los compara con cmp.Agilizando las comparaciones de archivos (con `cmp`) en Cygwin?

Desafortunadamente, esto parece funcionar abysmally lentamente - tomando aproximadamente diez (Edición: en realidad 25!) Veces el tiempo que lleva generar uno de los conjuntos de archivos usando un programa de Python.

¿Estoy en lo cierto al pensar que esto es sorprendentemente lento? ¿Hay alguna alternativa simple que iría más rápido?

(Para elaborar un poco en mi caso de uso: estoy autogenerating un montón de .c archivos en un directorio temporal, y cuando me volver a generarlas, me gustaría copiar solamente los que han cambiado en el directorio de origen real, dejando intactos los cambios (con sus viejos tiempos de creación) para que make sabrá que no necesita recompilarlos. No todos los archivos generados son .c, así que tengo que hacer binarios comparaciones en lugar de comparaciones de texto.)

+0

Por lo que vale, he confirmado que 'rsync' no hace lo que quiero; siempre restablece la hora modificada en archivos sin cambios, ya sea en la marca de tiempo en el original (si se llama con '-t') o en el momento de la transferencia. –

+0

Problema interesante ... Creo que también vale la pena preguntarse cómo evitar que la sincronización actualice la marca de tiempo de un archivo que no se ha transferido –

+0

@MiserableVariable: Buena idea, eso es ahora http://unix.stackexchange.com/questions/29845/ how-can-i-do-a-copy-if-changed-operation. (No creo que sea un duplicado, este es "¿cómo puedo escribir mejor este programa?", Mientras que eso es "¿puedo evitarlo por completo?") –

Respuesta

3

¿Tal vez debería usar Python para hacer algunas, o incluso todas, del trabajo de comparación también?

Una mejora sería solo molestar corriendo cmp si los tamaños de archivo son los mismos; si son diferentes, claramente el archivo ha cambiado. En lugar de ejecutar cmp, podría pensar en generar un hash para cada archivo, usando MD5 o SHA1 o SHA-256 o lo que le apetezca (usando módulos o extensiones de Python, si ese es el término correcto). Si no cree que va a tener intenciones maliciosas, entonces MD5 probablemente sea suficiente para identificar las diferencias.

Incluso en un script de shell, puede ejecutar un comando de hash externo y darle los nombres de todos los archivos en un directorio, y luego darle los nombres de todos los archivos en el otro directorio. Luego puede leer los dos conjuntos de valores hash más los nombres de archivo y decidir qué ha cambiado.

Sí, parece que tarda demasiado. Pero el problema incluye tener que lanzar 1000 copias de cmp, más el otro procesamiento. Tanto la sugerencia de secuencia de comandos de Python como la anterior tienen en común que evitan ejecutar un programa 1000 veces; intentan minimizar la cantidad de programas ejecutados. Esta reducción en el número de procesos ejecutados le dará un gran golpe para usted, supongo.


Si usted puede mantener los valores hash de 'el conjunto actual de archivos' alrededor y simplemente generar nuevos valores hash para el nuevo conjunto de archivos, y luego compararlos, usted hará bien. Claramente, si falta el archivo que contiene los 'hashes antiguos' (conjunto actual de archivos), tendrá que volver a generarlo a partir de los archivos existentes. Esto es información que se desarrolla un poco en los comentarios.

Otra posibilidad: puede hacer un seguimiento de los cambios en los datos que utiliza para generar estos archivos y usarlos para indicar qué archivos han cambiado (o, al menos, limitar el conjunto de archivos que pueden haber cambiado y por lo tanto, debe ser comparado, ya que sus comentarios indican que la mayoría de los archivos son iguales cada vez).

+2

El beneficio de la versión MD5 y otras versiones hash es que puedes almacenar el hash en lugar de realizar una comparación byte a byte en la _next_ iteración. Si vuelve a calcular el MD5 cada vez para los archivos antiguos y los nuevos archivos, no es muy diferente. – sarnold

+0

De acuerdo, ese es claramente el problema. Ejecutar 'md5sum *' en los archivos en uno de los directorios es mucho más rápido, y (como @sarnold implica) el único beneficio "real" que tiene es que solo está comenzando un programa; está cargando los mismos datos. Ahora, para ver si hay una manera bastante fácil de llegar desde allí a lo que necesito ... –

+0

@sarnold: Correcto - en la versión de Python, no tiene sentido calcular hashes MD5 si no se almacenan. –

1

Si puede hacer razonablemente la comparación de un millar de archivos impares dentro de un proceso en lugar de generar y ejecutar miles de programas adicionales, eso probablemente sería ideal.

La respuesta corta: Agregue --silent a su llamada cmp, si no está allí ya.

Es posible que pueda acelerar la versión de Python haciendo algunas comprobaciones de tamaño de archivo antes de verificar los datos.

primer lugar, un rápido y hacky bash(1) técnica que podría ser mucho más fácil si se puede cambiar a un único directorio build: utilizar la prueba bash-N:

$ echo foo > file 
$ if [ -N file ] ; then echo newer than last read ; else echo older than last read ; fi 
newer than last read 
$ cat file 
foo 
$ if [ -N file ] ; then echo newer than last read ; else echo older than last read ; fi 
older than last read 
$ echo blort > file # regenerate the file here 
$ if [ -N file ] ; then echo newer than last read ; else echo older than last read ; fi 
newer than last read 
$ 

Por supuesto, si algún subconjunto de la los archivos dependen de algún otro subconjunto de los archivos generados; este enfoque no funcionará en absoluto. (Esto podría ser una razón suficiente para evitar esta técnica, sino que depende de usted.)

Dentro de su programa de Python, también se puede comprobar el archivo tamaños usando os.stat() para determinar si debe o no llamar a su comparación rutina; si los archivos son de diferentes tamaños, realmente no le importa qué bytes cambiaron, por lo que puede omitir la lectura de ambos archivos. (Esto sería difícil de hacer en bash(1) - No conozco ningún mecanismo para obtener el tamaño del archivo en bash(1) sin ejecutar otro programa, lo que contradice el punto central de esta comprobación.)

El programa cmp va a hacer la comparación del tamaño internamente IFF está utilizando el --silent bandera y ambos archivos son archivos regulares y ambos archivos están ubicados en el mismo lugar. (Esto se establece mediante el indicador --ignore-initial). Si no está usando --silent, agréguela y vea cuál es la diferencia.

+1

Por lo que vale, estoy usando '--silent'.Además, debo tener en cuenta que esto realmente necesita ser optimizado para el caso de "casi todos los archivos coinciden" - la mayoría de las veces cuando llamo esto, solo he cambiado un puñado de los archivos generados. No obstante, consejos útiles. –

Cuestiones relacionadas