Найти и удалить дубликаты имен файлов в иерархии каталогов

#!/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 = $ '\ п'

Другие вопросы по тегам