Hay bastantes problemas en su script.
En primer lugar, con el fin de asignar el resultado de un comando a una variable que necesita para encerrarlo ya sea en backtics ( `command`
) o, preferiblemente, $(command)
. Lo tiene entre comillas simples ( 'command'
) que, en lugar de asignar el resultado de su comando a su variable, asigna el comando en sí como una cadena. Por lo tanto, tu test
es en realidad:
$ echo "test $sum1=$sum2"
test find $i -type f -iname "*.jpg" -exec md5sum {} \;=find $j -type f -iname "*.jpg" -exec md5sum {} \;
El siguiente problema es que el comando md5sum
devuelve más que solo el hash:
$ md5sum /etc/fstab
46f065563c9e88143fa6fb4d3e42a252 /etc/fstab
Solo desea comparar el primer campo, por lo que debe analizar el md5sum
resultado pasándolo a través de un comando que solo imprime el primer campo:
find $i -type f -iname "*.png" -exec md5sum '{}' \; | cut -f 1 -d ' '
o
find $i -type f -iname "*.png" -exec md5sum '{}' \; | awk '{print $1}'
Además, el find
comando devolverá muchas coincidencias, no solo una y cada una de esas coincidencias se duplicará por la segunda find
. Esto significa que en algún momento comparará el mismo archivo consigo mismo, el md5sum será idéntico y terminará eliminando todos sus archivos (ejecuté esto en un directorio de prueba que contiene a.jpg
y b.jpg
):
for i in $(find . -iname "*.jpg"); do
for j in $(find . -iname "*.jpg"); do
echo "i is: $i and j is: $j"
done
done
i is: ./a.jpg and j is: ./a.jpg ## BAD, will delete a.jpg
i is: ./a.jpg and j is: ./b.jpg
i is: ./b.jpg and j is: ./a.jpg
i is: ./b.jpg and j is: ./b.jpg ## BAD will delete b.jpg
No desea ejecutar a for i in directory_path
menos que esté pasando una serie de directorios. Si todos estos archivos están en el mismo directorio, desea ejecutar for i in $(find directory_path -iname "*.jpg"
) para revisar todos los archivos.
Es una mala idea usar for
bucles con la salida de find. Debe usar while
bucles o globbing :
find . -iname "*.jpg" | while read i; do [...] ; done
o, si todos sus archivos están en el mismo directorio:
for i in *jpg; do [...]; done
Dependiendo de su shell y las opciones que haya configurado, puede usar globbing incluso para archivos en subdirectorios, pero no entremos en eso aquí.
Finalmente, también debe citar sus variables; de lo contrario, las rutas de directorio con espacios romperán su script.
Los nombres de archivo pueden contener espacios, nuevas líneas, barras invertidas y otros caracteres extraños, para tratarlos correctamente en un while
bucle necesitará agregar algunas opciones más. Lo que quieres escribir es algo como:
find dir_path -type f -iname "*.jpg" -print0 | while IFS= read -r -d '' i; do
find dir_path -type f -iname "*.jpg" -print0 | while IFS= read -r -d '' j; do
if [ "$i" != "$j" ]
then
sum1=$(md5sum "$i" | cut -f 1 -d ' ' )
sum2=$(md5sum "$j" | cut -f 1 -d ' ' )
[ "$sum1" = "$sum2" ] && rm "$j"
fi
done
done
Una forma aún más simple sería:
find directory_path -name "*.jpg" -exec md5sum '{}' + |
perl -ane '$k{$F[0]}++; system("rm $F[1]") if $k{$F[0]}>1'
Una mejor versión que puede manejar espacios en los nombres de archivo:
find directory_path -name "*.jpg" -exec md5sum '{}' + |
perl -ane '$k{$F[0]}++; system("rm \"@F[1 .. $#F]\"") if $k{$F[0]}>1'
Este pequeño script de Perl se ejecutará a través de los resultados del find
comando (es decir, el md5sum y el nombre del archivo). La -a
opción para perl
dividir líneas de entrada en espacios en blanco y guardarlas en la F
matriz, $F[0]
será md5sum y $F[1]
el nombre del archivo. El md5sum se guarda en el hash k
y el script comprueba si el hash ya se ha visto ( if $k{$F[0]}>1
) y elimina el archivo si lo tiene ( system("rm $F[1]")
).
Si bien eso funcionará, será muy lento para grandes colecciones de imágenes y no puede elegir qué archivos guardar. Hay muchos programas que manejan esto de una manera más elegante, incluyendo: