У вашому сценарії існує досить багато проблем.
По- перше, для того , щоб призначити результат виконання команди на змінну ви повинні укласти його або в backtics ( `command`) або, переважно, $(command). Ви маєте його в одиничних лапках ( 'command'), які замість присвоєння результату вашої команди вашій змінній призначає саму команду як рядок. Отже, ваш testнасправді:
$ echo "test $sum1=$sum2"
test find $i -type f -iname "*.jpg" -exec md5sum {} \;=find $j -type f -iname "*.jpg" -exec md5sum {} \;
Наступне питання полягає в тому, що команда md5sumповертає більше, ніж просто хеш:
$ md5sum /etc/fstab
46f065563c9e88143fa6fb4d3e42a252 /etc/fstab
Ви хочете лише порівняти перше поле, тому вам слід проаналізувати md5sumвихід, передавши його через команду, яка друкує лише перше поле:
find $i -type f -iname "*.png" -exec md5sum '{}' \; | cut -f 1 -d ' '
або
find $i -type f -iname "*.png" -exec md5sum '{}' \; | awk '{print $1}'
Крім того, findкоманда поверне багато матчів, а не лише одне, і кожне з них буде дублюватися другим find. Це означає, що в якийсь момент ви будете порівнювати той самий файл із самим собою, md5sum буде ідентичним, і ви в кінцевому підсумку видалите всі свої файли (я запустив це на тестовому режимі, що містить a.jpgі 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
Ви не хочете запускатись, for i in directory_pathякщо не передаєте масив каталогів. Якщо всі ці файли в одному каталозі, ви хочете запустити for i in $(find directory_path -iname "*.jpg"), щоб пройти всі файли.
Дуже погано використовувати forпетлі з результатом знаходження. Ви повинні використовувати whileпетлі або глобус :
find . -iname "*.jpg" | while read i; do [...] ; done
або, якщо всі ваші файли знаходяться в одному каталозі:
for i in *jpg; do [...]; done
Залежно від вашої оболонки та параметрів, які ви встановили, ви можете використовувати глобулінг навіть для файлів у підкаталогах, але не будемо тут вникати.
Нарешті, ви також повинні навести ваші змінні шляхи до каталогу, які містять пробіли, розбивають ваш сценарій.
Імена файлів можуть містити пробіли, нові рядки, зворотні косої риси та інші дивні символи, щоб правильно з ними працювати в whileциклі, вам потрібно буде додати ще кілька варіантів. Те, що ви хочете написати, - це щось на зразок:
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
Ще простішим способом було б:
find directory_path -name "*.jpg" -exec md5sum '{}' + |
perl -ane '$k{$F[0]}++; system("rm $F[1]") if $k{$F[0]}>1'
Краща версія, яка може мати пробіли у назвах файлів:
find directory_path -name "*.jpg" -exec md5sum '{}' + |
perl -ane '$k{$F[0]}++; system("rm \"@F[1 .. $#F]\"") if $k{$F[0]}>1'
Цей маленький скрипт Perl запустить результати findкоманди (тобто md5sum та ім'я файлу). -aВаріант для perlрозколів вхідних ліній пробільних і зберігає їх в Fмасиві, так $F[0]буде md5sum і $F[1]ім'я файлу. Md5sum зберігається в хеші, kі скрипт перевіряє, чи хеш уже бачили ( if $k{$F[0]}>1), і видаляє файл, якщо він має ( system("rm $F[1]")).
Хоча це працюватиме, для великих колекцій зображень буде дуже повільно, і ви не можете вибрати, які файли зберігати. Існує багато програм, які вирішують цю проблему більш елегантно, зокрема: