Convierta archivos idénticos a enlaces físicos

Convierta archivos idénticos a enlaces físicos

Tengo mucha música en un árbol bajo un directorio, guardada en cualquier formato en el que la obtuve inicialmente, por calidad. Tengo un segundo árbol de directorios que tiene una estructura similar, pero con todos los archivos en un formato comprimido con pérdida que mi teléfono puede reproducir y con cambios ocasionales de metadatos (por ejemplo, eliminando cubiertas incrustadas para ahorrar espacio).

Se me ocurre que para una parte importante de la música, no hay diferencia entre los dos casos - generalmente cuando la versión distribuida sólo estaba disponible como mp3/ogg y no tenía carátulas integradas. El espacio en el disco duro puede ser barato, pero no es motivo para desperdiciarlo. ¿Hay alguna manera de escribir un guión?

  1. Busque archivos idénticos en dos directorios
  2. Siempre que se encuentren archivos idénticos, reemplace uno con un enlace físico al otro
  3. Sin, por ejemplo, tomarse el tiempo para obtener una diferencia completa, en aras del tiempo
  4. Pero aún sin el riesgo de eliminar accidentalmente una copia de dos archivos no idénticos, lo cual es una posibilidad remota pero distinta de cero si, por ejemplo, simplemente comparara hashes.

Respuesta1

Lo siguiente se utiliza md5para producir un resumen MD5 de todos los archivos en el directorio actual o debajo:

find . -type f -exec md5 {} +

Reemplácelo md5con md5sum --tagsi no tiene la md5utilidad BSD.

Construyamos un script simple para hacer eso en los directorios:

#!/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 directorios en la línea de comando y produce archivos llamados md5.1y md5.2, un archivo para cada directorio, en /tmp(o dondequiera que $TMPDIRapunte). Estos archivos están ordenados en el resumen MD5.

Los archivos se verán como

MD5 (<path>) = <MD5 digest>

con una de esas líneas para cada archivo.

Luego, en el mismo script, compare la suma de comprobación entre los dos archivos:

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

Esto realiza una operación de "unión" relacional entre los dos archivos, utilizando la suma de comprobación como campo de unión. Cualquier línea que tenga la misma suma de verificación en los dos campos se fusionará y generará.

Si alguna suma de verificación es la misma en ambos archivos, esto generará:

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

Esto se puede pasar awkdirectamente para analizar las dos rutas:

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

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

Esto daría como resultado

<path1><tab><path2>

Entonces es sólo cuestión de leer estos pares de rutas separadas por tabulaciones y emitir los comandos 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 echoin the whileloop está ahí por seguridad. Ejecútelo una vez para ver qué sucedería, elimínelo y ejecútelo nuevamente si está seguro de que está haciendo lo correcto.

Recuerde que los enlaces físicos no pueden abarcar particiones. Esto significa que ambos directorios deben residir en la misma partición. Los archivos en elsegundoEl directorio se sobrescribirá si se encuentran duplicados. ¡Mantenga una copia de seguridad de los originales en algún lugar hasta que esté satisfecho con el resultado!

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

Respuesta2

A menos que tenga una gran colección de archivos muy similares, calcular y comparar hashes no acelera el proceso de búsqueda de duplicados. La operación más lenta es la lectura del disco. Calcular un hash significa leer el archivo completo y también es una tarea que requiere un uso intensivo de la CPU con hashes modernos criptográficamente fuertes.

Tenemos que comparar datos sólo si las longitudes de los archivos difieren. Si sólo hay un archivo con una longitud determinada, obviamente no hay duplicados. Si hay dos, simplemente compararlos siempre es más eficiente que el hash. Si hay tres o más, el número de comparaciones aumenta, pero es probable que difieran en los primeros bytes o bloques, por lo que la E/S del disco sigue siendo baja y se devuelven lecturas repetidas desde la caché.

Es por eso que recomendaría hacer una lista de directorios recursiva preparando una lista de longitud + nombre de ruta, luego ordenar la lista numéricamente y finalmente tratar solo con conjuntos de archivos que comparten la misma longitud comparándolos por pares. Si dos archivos coinciden, uno puede ser reemplazado por un vínculo físico.

información relacionada