Найти и удалить дубликаты имен файлов в иерархии каталогов
#!/bin/sh
LASTBASE=""
find $1 -type f -print | rev | sort | rev | while read FILE
do
BASE=$(basename "$FILE")
if [ "$BASE" = "$LASTBASE" ]; then
rm "$FILE"
LASTBASE="$BASE"
done
3 ответа
Если вы передаете вывод find
в while read
Цикл вы можете иметь дело с ними построчно:
find nnn/ -type f -print | rev | sort | rev | while read FILE; do
...
done
Изменить: Таким образом, этот метод не работает, если имена файлов содержат двойные (последовательные) пробелы, потому что read
на самом деле разбивает линию в соответствии с $IFS
и затем присоединяется к нему снова при сохранении последней переменной. Для решения этой проблемы вы можете временно изменить $IFS
отключить расщепление:
OIFS="$IFS"
IFS=""
find | while read...
IFS="$OIFS"
Редактировать: test
(что так же, как [
) не имеет ==
оператор, вы просто хотите =
,
Я только что нашел этот "драгоценный камень" в старой истории bash, и он, на самом деле, работает, не спотыкаясь о пробелах в именах файлов.
Контентное сравнение
for hash in `find . -exec md5sum {} \; 2>/dev/null | sort | awk '{ print $1 }' | uniq -d`; do
find . -exec md5sum {} \; 2>/dev/null | grep $hash | awk '{print $2 }';
done;
неофициальный:
- Первая строка: пройти по дереву каталогов и вычислить сумму md5 всех файлов ниже, отсортировать этот вывод (формат: имя файла хэша), взять столбец хэша, уменьшить его до удвоенных значений. (означает, что есть дубликаты)
- Вторая строка: для каждого из двойных хэшей повторите ход и напечатайте имя файла, если текущий файл имеет текущий хеш (означает, что файл является одним из нескольких)
пример вывода:
./aFile
./aFolder/aFile
./1000digitsOfPI
./a/b/c/thousanddigitsofPI
./b File
./bFolder/cFolder/b File
Удаление здесь не реализовано, поскольку может быть трудно решить, какую версию дублированных файлов вы хотите сохранить.
Сравнение по имени файла
Если вы просто хотите посмотреть на имена файлов, а не на содержимое, это станет еще проще:
for name in `find . -type f -printf "%f\n" | sort | uniq -d`; do
find . -name $name;
done;
Обновление: К сожалению, эта версия снова ломается с пробелами в именах файлов.
Проблема заключается в этой строке кода for FILE in $FILES; do
- цикл for назначает переменную FILE на основе разделителя пробелов. Поэтому, если файл имеет один или несколько пробелов, он не будет работать. Просто измените IFS по умолчанию с пробела на новую строку или вкладку. Если я правильно помню, вы можете установить IFS в Bash, используя что-то вроде этого -
IFS = $ '\ п'