Existem alguns problemas no seu script.
Primeiro, para atribuir o resultado de um comando a uma variável, você deve incluí-lo em backtics ( `command`
) ou, preferencialmente $(command)
,. Você o coloca entre aspas simples ( 'command'
) que, em vez de atribuir o resultado do seu comando à sua variável, atribui o próprio comando como uma string. Portanto, você test
é realmente:
$ echo "test $sum1=$sum2"
test find $i -type f -iname "*.jpg" -exec md5sum {} \;=find $j -type f -iname "*.jpg" -exec md5sum {} \;
O próximo problema é que o comando md5sum
retorna mais do que apenas o hash:
$ md5sum /etc/fstab
46f065563c9e88143fa6fb4d3e42a252 /etc/fstab
Você deseja comparar apenas o primeiro campo, portanto, deve analisar a md5sum
saída passando-a por um comando que imprime apenas o primeiro campo:
find $i -type f -iname "*.png" -exec md5sum '{}' \; | cut -f 1 -d ' '
ou
find $i -type f -iname "*.png" -exec md5sum '{}' \; | awk '{print $1}'
Além disso, o find
comando retornará muitas correspondências, não apenas uma e cada uma dessas correspondências será duplicada a cada segundo find
. Isso significa que em algum momento você estará comparando o mesmo arquivo, o md5sum será idêntico e você acabará excluindo todos os seus arquivos (eu executei isso em um diretório de teste contendo a.jpg
e 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
Você não deseja executar, a for i in directory_path
menos que esteja passando uma matriz de diretórios. Se todos esses arquivos estiverem no mesmo diretório, você deseja executar for i in $(find directory_path -iname "*.jpg"
) para passar por todos os arquivos.
É uma má idéia usar for
loops com a saída do find. Você deve usar while
loops ou globbing :
find . -iname "*.jpg" | while read i; do [...] ; done
ou, se todos os seus arquivos estiverem no mesmo diretório:
for i in *jpg; do [...]; done
Dependendo do seu shell e das opções que você definiu, você pode usar globbing mesmo para arquivos em subdiretórios, mas não vamos entrar aqui.
Por fim, você também deve citar suas variáveis, caso contrário, os caminhos de diretório com espaços quebrarão seu script.
Os nomes de arquivos podem conter espaços, novas linhas, barras invertidas e outros caracteres estranhos. Para lidar com aqueles corretamente em um while
loop, você precisará adicionar mais algumas opções. O que você deseja escrever é 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
Uma maneira ainda mais simples seria:
find directory_path -name "*.jpg" -exec md5sum '{}' + |
perl -ane '$k{$F[0]}++; system("rm $F[1]") if $k{$F[0]}>1'
Uma versão melhor que pode lidar com espaços nos nomes dos arquivos:
find directory_path -name "*.jpg" -exec md5sum '{}' + |
perl -ane '$k{$F[0]}++; system("rm \"@F[1 .. $#F]\"") if $k{$F[0]}>1'
Este pequeno script Perl executará os resultados do find
comando (ou seja, o md5sum e o nome do arquivo). A -a
opção para perl
dividir as linhas de entrada no espaço em branco e salvá-las na F
matriz, assim $F[0]
será o md5sum e $F[1]
o nome do arquivo. O md5sum é salvo no hash k
e o script verifica se o hash já foi visto ( if $k{$F[0]}>1
) e exclui o arquivo se tiver ( system("rm $F[1]")
).
Embora isso funcione, será muito lento para grandes coleções de imagens e você não poderá escolher quais arquivos manter. Existem muitos programas que lidam com isso de uma maneira mais elegante, incluindo: