Convierte files idénticos a enlaces duros

Tengo mucha música en un tree bajo un directory, guardado en cualquier formatting en el que inicialmente lo obtuve, por calidad. Tengo un segundo tree de directorys que es similar en estructura, pero con todos los files en un formatting comprimido con pérdida que puede reproducir mi teléfono, y con cambios ocasionales en los metadatos (por ejemplo, quitar las cubiertas incrustadas para ahorrar espacio).

Se me ocurre que para una parte importante de la música, no hay diferencia entre las dos instancias, generalmente cuando la versión distribuida solo estaba disponible como mp3 / ogg y no tenía cubiertas integradas. El espacio en el disco duro puede ser barato, pero no hay razón para desperdiciarlo. ¿Hay alguna forma de script?

  1. Compruebe si hay files idénticos en dos directorys
  2. Siempre que se encuentren files idénticos, reemplace uno con un enlace rígido al otro
  3. Sin, por ejemplo, tomarse el time para get una diferencia completa, en aras del time
  4. Pero aún así, sin el riesgo de eliminar accidentalmente una copy de dos files no idénticos, lo cual es una posibilidad remota pero distinta de cero, si tuviera que comparar, por ejemplo, hashes.

Lo siguiente usa md5 para producir un compendio de MD5 para todos los files en el directory actual o a continuación:

 find . -type f -exec md5 {} + 

Reemplace md5 con md5sum --tag si no tiene la utilidad BSD md5 .

Construyamos un script simple para hacer eso en los directorys:

 #!/bin/bash tmpdir=${TMPDIR:-/tmp} if (( $# != 2 )); then echo 'Expected two directories as arguments' >&2 exit 1 fi i=0 for dir in "$@"; do (( ++i )) find "$dir" -type f -exec md5 {} + | sort -t '=' -k2 -o "$tmpdir/md5.$i" done 

Esto toma dos directorys en la línea de command y produce files llamados md5.1 y md5.2 , un file para cada directory, en /tmp (o donde sea que $TMPDIR esté apuntando). Estos files están orderados en el resumen MD5.

Los files se verán como

 MD5 (<path>) = <MD5 digest> 

con una de esas líneas para cada file.

Luego, en el mismo script, compare la sum de verificación entre los dos files:

 join -t '=' -1 2 -2 2 "$tmpdir"/md5.[12] 

Esto hace una operación relacional de "unión" entre los dos files, usando la sum de comprobación como el campo de unión. Cualquier línea que tenga la misma sum de comprobación en los dos campos se fusionará y se generará.

Si cualquier sum de comprobación es la misma en ambos files, se generará:

 <space><MD5 digest>=MD5 (<path1>) =MD5 (<path2>) 

Esto se puede pasar a awk directamente para analizar las dos routes:

 awk -F '[()]' 'BEGIN { OFS="\t" } { print $2, $4 }' 

El -F [()] es solo una forma de decir que nos gustaría dividir cada línea en campos basados ​​en ( y ) . Hacer esto nos deja con los paths en los campos 2 y 4.

Esto produciría

 <path1><tab><path2> 

Entonces, solo es cuestión de leer estos pares de routes separadas por tabuladores y emitir los commands correctos para crear los enlaces:

 while IFS=$'\t' read -r path1 path2; do echo ln -f "$path1" "$path2" done 

En resumen:

 #!/bin/bash tmpdir=${TMPDIR:-/tmp} if (( $# != 2 )); then echo 'Expected two directories as arguments' >&2 exit 1 fi i=0 for dir in "$@"; do (( ++i )) find "$dir" -type f -exec md5 {} + | sort -t '=' -k2 -o "$tmpdir/md5.$i" done join -t '=' -1 2 -2 2 "$tmpdir"/md5.[12] | awk -F '\\)|\\(' 'BEGIN { OFS="\t" } { print $2, $4 }' | while IFS=$'\t' read -r path1 path2; do echo ln -f "$path1" "$path2" done rm -f "$tmpdir"/md5.[12] 

El echo en el ciclo while está ahí por security. Ejecútelo una vez para ver qué sucedería, y quítelo y ejecútelo de nuevo si está seguro de que está haciendo lo correcto.

Recuerde que los enlaces duros no pueden abarcar las particiones. Esto significa que ambos directorys necesitan vivir en la misma partición. Los files en el segundo directory se sobrescribirán si se encuentran duplicates. Mantenga una copy de security de los originales en alguna parte hasta que esté satisfecho con el resultado.

Tenga en count que esta solución no funcionará correctamente si algún file tiene ( o ) o una pestaña en sus nombres de file.

A less que tenga una gran colección de files muy similares, la computación y la comparación de hashes no acelera el process de búsqueda de duplicates. La operación más lenta es una lectura de disco. Computar un hash significa leer todo el file y también es una tarea intensiva de CPU con hash criptográficamente fuertes modernos.

Tenemos que comparar los datos solo si las longitudes de los files son diferentes. Si solo hay un file con una longitud dada, obviamente no hay duplicates. Si hay dos, simplemente compararlos siempre es más eficiente que el hash. Si hay tres o más, la cantidad de comparaciones aumenta, pero es posible que difieran en los primeros bytes o bloques, por lo que la E / S del disco sigue siendo baja y las lecturas repetidas se devuelven desde la memory caching.

Es por eso que recomendaría hacer una list de directorys recursivos para preparar una list de longitud + nombre de ruta, luego orderar la list numéricamente y finalmente tratar solo con sets de files que comparten la misma longitud al compararlos en pares. Si dos files coinciden, uno puede ser reemplazado por un enlace fijo.