Existem alguns problemas no seu script.
-
Primeiro, para atribuir o resultado de um comando a uma variável, é necessário incluí-lo no backtics (
'command'
) ou, preferencialmente,$(command)
. Você tem isso entre aspas simples ('command'
), que ao invés de atribuir o resultado do seu comando à sua variável, atribui o próprio comando como uma string. Portanto, seutest
é, na verdade:$ echo "test $sum1=$sum2" test find $i -type f -iname "*.jpg" -exec md5sum {} \;=find $j -type f -iname "*.jpg" -exec md5sum {} \;
-
A próxima questão é que o comando
md5sum
retorna mais do que apenas o hash:$ md5sum /etc/fstab 46f065563c9e88143fa6fb4d3e42a252 /etc/fstab
Você só deseja comparar o primeiro campo, portanto, deve analisar o
md5sum
output passando-o 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 comando
find
retornará muitas correspondências, não apenas uma e cada uma dessas correspondências será duplicada pela segundafind
. Isso significa que em algum momento você estará comparando o mesmo arquivo a si mesmo, o md5sum será idêntico e você acabará excluindo todos seus arquivos (executei isso em um diretório de teste contendoa.jpg
eb.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
for i in directory_path
, a menos que esteja passando uma matriz de diretórios. Se todos esses arquivos estiverem no mesmo diretório, você deseja executarfor i in $(find directory_path -iname "*.jpg"
) para percorrer todos os arquivos. -
É uma má idéia de usar
for
loops com a saída de encontrar. Você deve usarwhile
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 até para arquivos em subdiretórios, mas não vamos entrar aqui.
-
Finalmente, você também deve citar suas variáveis, senão os caminhos de diretório com espaços irão quebrar seu script.
Os nomes dos arquivos podem conter espaços, novas linhas, barras invertidas e outros caracteres estranhos, para lidar com eles corretamente em um loop while
, você precisará adicionar mais algumas opções. O que você quer 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 em nomes de arquivos:
find directory_path -name "*.jpg" -exec md5sum '{}' + |
perl -ane '$k{$F[0]}++; system("rm \"@F[1 .. $#F]\"") if $k{$F[0]}>1'
Esse pequeno script Perl será executado pelos resultados do comando find
(ou seja, o md5sum e o nome do arquivo). A opção -a
para perl
divide as linhas de entrada no espaço em branco e as salva na matriz F
, portanto $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]")
).
Enquanto isso vai funcionar, será muito lento para grandes coleções de imagens e você não pode escolher quais arquivos manter. Existem muitos programas que lidam com isso de uma maneira mais elegante, incluindo: