Назад | Перейти на главную страницу

найти и удалить повторяющиеся имена файлов в иерархии каталогов

#!/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

Если вы направите вывод 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;

неофициальный:

  • Первая строка: пройдитесь по дереву каталогов и вычислите md5sum всех файлов ниже, отсортируйте этот вывод (формат: хеш-имя файла), возьмите столбец хэша, уменьшите его до удвоенных значений. (значит есть дубликаты)
  • Вторая строка: для каждого из двукратных хэшей, пройдитесь снова и распечатайте имя файла, если текущий файл имеет текущий хеш (означает, что файл является одним из нескольких)

пример вывода:

./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 = $ '\ n'