sábado, 25 de octubre de 2014

Desduplicar archivos idénticos en Linux

Recientemente tuve que reconstruir mi colección de música a partir de mi reproductor varios respaldos parciales (en un reproductor de música, en una memoria USB, una computadora vieja, etc), y aparte del problema de tener que categorizar de nuevo algunas canciones, me encontré con que un mismo archivo estaba en diferentes carpetas o rutas en los diversos soportes, por lo cual colocar todas las subcarpetas en un mismo directorio resultaba en un que una misma canción podía estar repetida varias veces.

Inicialmente, quise utilizar los metadatos de las canciones para detectar los archivos duplicados, pero era un trabajo manual muy tedioso ordenar por artista e ir leyendo cada una, así que decidí hacer un guión en bash.

A continuación el método a grandes rasgos:
  1. Para detectar si un archivo está repetido, se hará una comparación de su huella SHA256, una función robusta de resumen criptográfico.
  2. Se comparan dos carpetas a la vez (en mi caso una carpeta con una colección parcial que estaba mejor estructurada que otra carpeta con canciones sueltas).
  3. Se ejecuta la función de SHA256 sobre todos los archivos de una carpeta y se almacena el nombre y la huella de cada archivo. Luego se ejecuta la operación con la otra carpeta.
  4. Se combinan ambos listados y para determinar si alguna huella está repetida.
  5. Para los casos cuando un archivo esté duplicado, en una carpeta se conservará y en la otra será eliminado. Por simplicidad, en una carpeta siempre se conservará y en la otra siempre se eliminará.
  6. Se utiliza la información de las huellas repetidas para determinar a cuales archivos se corresponden en la carpeta para eliminar.
  7. Se borran masivamente los archivos repetidos en la carpeta para borrar.
El guión quedó así:
#!/bin/sh

# Autor: J. Navarro
# Fecha: 2014
# Este programa se distribuye bajo licencia GPLv2
# Puede obtener el texto completo de la licencia en:
# http://www.gnu.org/licenses/gpl-2.0.html


#Inicialización de variables

NOBORRARDIR=<carpeta donde se conservan los duplicados>;
NOBORRARTMP="/tmp/noborrar.sha256";
NOBORRARREP="/tmp/noborrar.sha256.repetido";

BORRABLEDIR=<carpeta de donde se eliminan los duplicados>;
BORRABLETMP="/tmp/borrable.sha256";
BORRABLEREP="/tmp/borrable.sha256.repetido"

REPETIDOTMP="/tmp/repetido.sha256"
REPETIDOMIX="/tmp/repetido-mix.sha256"

#Ejecución de comandos

echo "Generando resumen criptografico de $NOBORRARDIR"

find $NOBORRARDIR -type f -exec sha256sum {} + > $NOBORRARTMP

echo "Generando resumen criptografico de $BORRABLEDIR"

find $BORRABLEDIR -type f -exec sha256sum {} + > $BORRABLETMP

echo "Detectando archivos repetidos"

 #listado de resumenes criptograficos de archivos repetidos
cat $NOBORRARTMP $BORRABLETMP | cut -f1 -d\ | sort | uniq -d \
> $REPETIDOTMP


#listado combinado donde los archivos repetidos estan juntos
cat $NOBORRARTMP $BORRABLETMP | sort | egrep -f $REPETIDOTMP \
> $REPETIDOMIX

# Cuando grep genera el error "Referencia hacia atrás inválida"
# cat $NOBORRARTMP $BORRABLETMP | grep -F -f $REPETIDOTMP | sort \

#> $REPETIDOMIX

#visualizar el listado de archivos repetidos
pager $REPETIDOMIX

echo "Archivos repetidos a conservar"

egrep $NOBORRARTMP -f $REPETIDOTMP > $NOBORRARREP
pager $NOBORRARREP

echo "Archivos repetidos a eliminar"

egrep $BORRABLETMP -f $REPETIDOTMP > $BORRABLEREP
pager $BORRABLEREP

# Se toman solamente los nombres de archivo,
# se encierran entre comillas y se borran.
cat /tmp/borrable.sha256.repetido | perl -pe "s/[^ ]+[ ]+//" | \
perl -pe 'chomp $_; $_="\"$_\"\n"' | xargs rm