W twoim skrypcie jest sporo problemów.
Po pierwsze, aby przypisać wynik polecenia do zmiennej, musisz ująć ją w backtics ( `command`
) lub, najlepiej, w $(command)
. Masz go w pojedynczych cudzysłowach ( 'command'
), które zamiast przypisywać wynik polecenia do zmiennej, przypisują samo polecenie jako ciąg znaków. Dlatego twoje test
jest w rzeczywistości:
$ echo "test $sum1=$sum2"
test find $i -type f -iname "*.jpg" -exec md5sum {} \;=find $j -type f -iname "*.jpg" -exec md5sum {} \;
Następnym problemem jest to, że polecenie md5sum
zwraca więcej niż tylko skrót:
$ md5sum /etc/fstab
46f065563c9e88143fa6fb4d3e42a252 /etc/fstab
Chcesz tylko porównać pierwsze pole, więc powinieneś przeanalizować dane md5sum
wyjściowe, przekazując je za pomocą polecenia, które wypisuje tylko pierwsze pole:
find $i -type f -iname "*.png" -exec md5sum '{}' \; | cut -f 1 -d ' '
lub
find $i -type f -iname "*.png" -exec md5sum '{}' \; | awk '{print $1}'
Ponadto find
polecenie zwróci wiele dopasowań, a nie tylko jedno, a każde z nich zostanie powtórzone przez drugi find
. Oznacza to, że w pewnym momencie będziesz porównywał ten sam plik z samym sobą, suma md5 będzie identyczna i skończysz na usuwaniu wszystkich plików (uruchomiłem to w katalogu testowym zawierającym a.jpg
i 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
Nie chcesz uruchamiać, for i in directory_path
chyba że przekazujesz tablicę katalogów. Jeśli wszystkie te pliki znajdują się w tym samym katalogu, chcesz uruchomić for i in $(find directory_path -iname "*.jpg"
), aby przejrzeć wszystkie pliki.
Nie jest dobrym pomysłem stosowanie for
pętli z wyjściem find. Powinieneś użyć while
pętli lub globowania :
find . -iname "*.jpg" | while read i; do [...] ; done
lub jeśli wszystkie twoje pliki znajdują się w tym samym katalogu:
for i in *jpg; do [...]; done
W zależności od powłoki i ustawionych opcji możesz używać globowania nawet dla plików w podkatalogach, ale nie wchodźmy w to tutaj.
Na koniec powinieneś również podać swoje zmienne, inaczej ścieżki katalogu ze spacjami spowodują uszkodzenie skryptu.
Nazwy plików mogą zawierać spacje, nowe linie, ukośniki odwrotne i inne dziwne znaki, aby poprawnie radzić sobie z nimi w while
pętli, musisz dodać więcej opcji. To, co chcesz napisać, to:
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
Jeszcze prostszym sposobem byłoby:
find directory_path -name "*.jpg" -exec md5sum '{}' + |
perl -ane '$k{$F[0]}++; system("rm $F[1]") if $k{$F[0]}>1'
Lepsza wersja, która radzi sobie ze spacjami w nazwach plików:
find directory_path -name "*.jpg" -exec md5sum '{}' + |
perl -ane '$k{$F[0]}++; system("rm \"@F[1 .. $#F]\"") if $k{$F[0]}>1'
Ten mały skrypt Perla przejdzie przez wyniki find
polecenia (tj. Md5sum i nazwa pliku). -a
Opcja dla perl
linii wejściowych dzieli na białych znaków i zapisuje je na F
tablicy, więc $F[0]
będzie md5sum i $F[1]
nazwa pliku. Wartość md5sum jest zapisywana w haszu, k
a skrypt sprawdza, czy hash został już wyświetlony ( if $k{$F[0]}>1
), i usuwa plik, jeśli ma ( system("rm $F[1]")
).
Chociaż to zadziała, będzie bardzo powolne w przypadku dużych kolekcji obrazów i nie można wybrać, które pliki zachować. Istnieje wiele programów, które obsługują to w bardziej elegancki sposób, w tym: