В вашем скрипте довольно много проблем.
Во-первых, чтобы присвоить результат команды переменной, необходимо заключить ее в 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
циклы с выводом find. Вы должны использовать 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]
имя файла. Сумма md5 сохраняется в хэше, k
и скрипт проверяет, был ли хэш уже просмотрен ( if $k{$F[0]}>1
), и удаляет файл, если он имеет ( system("rm $F[1]")
).
Хотя это будет работать, это будет очень медленно для больших коллекций изображений, и вы не сможете выбрать, какие файлы сохранить. Есть много программ, которые обрабатывают это более элегантным способом, включая: