бездисковый поиск дубликатов имен файлов

У меня есть способ найти все файлы в каталоге с повторяющимися именами файлов, независимо от корпуса (в верхнем и / или нижнем регистре)?

Если у вас есть утилиты GNU (или, по крайней мере, набор, который может работать с линиями с нулевым завершением), другой ответ имеет отличный метод:

find . -maxdepth 1 -print0 | sort -z | uniq -diz 

Примечание: вывод будет иметь строки с нулевым завершением; инструмент, который вы используете для дальнейшего его обработки, должен иметь возможность справиться с этим.

В отсутствие инструментов, которые работают с линиями с нулевым завершением или если вы хотите убедиться, что ваш код работает в средах, где такие инструменты недоступны, вам нужен небольшой скрипт:

 #!/bin/sh for f in *; do find . -maxdepth 1 -iname ./"$f" -exec echo \; | wc -l | while read count; do [ $count -gt 1 ] && echo $f done done 

Что это за безумие? См. Этот ответ для объяснения методов, которые делают это безопасным для сумасшедших имен файлов.

Есть много сложных ответов выше, это кажется более простым и быстрым, чем все из них:

 find . -maxdepth 1 | sort -f | uniq -di 

Если вы хотите найти дубликаты имен файлов в подкаталогах, вам нужно сравнить только имя файла, а не весь путь:

 find . -maxdepth 2 -printf "%f\n" | sort -f | uniq -di 

Edit: Shawn J. Goff указал, что это не удастся, если у вас есть имена файлов с символами новой строки. Если вы используете утилиты GNU, вы также можете сделать эту работу:

 find . -maxdepth 1 -print0 | sort -fz | uniq -diz 

Параметры -print0 (для поиска) и -z (для sort и uniq) заставляют их работать с строками с завершающим нулем, вместо строк с завершающим символом строки. Поскольку имена файлов не могут содержать NUL, это работает для всех имен файлов.

Отсортируйте список имен файлов в режиме без учета регистра и распечатывайте дубликаты. sort имеет возможность сортировки без учета регистра. Так же GNU uniq , но не другие реализации, и все, что вы можете сделать с uniq – это распечатать каждый элемент из набора дубликатов, кроме первого, что встречается. С помощью инструментов GNU, предполагая, что имя файла не содержит новую строку, есть простой способ распечатать все элементы, кроме одного в каждом наборе дубликатов:

 for x in *; do printf "%s\n" "$x"; done | sort -f | uniq -id 

Портативно, чтобы печатать все элементы в каждом наборе дубликатов, предполагая, что ни одно имя файла не содержит новую строку:

 for x in *; do printf "%s\n" "$x"; done | sort -f | awk ' tolower($0) == tolower(prev) { print prev; while (tolower($0) == tolower(prev)) {print; getline} } 1 { prev = $0 }' 

Если вам нужно разместить имена файлов, содержащие новые строки, перейдите на Perl или Python. Обратите внимание, что вам может потребоваться настроить выход или лучше выполнить дальнейшую обработку на одном языке, так как примерный код ниже использует новые строки для разделения имен в своем собственном выходе.

 perl -e ' foreach (glob("*")) {push @{$f{lc($_)}}, $_} foreach (keys %f) {@names = @{$f{$_}}; if (@names > 1) {print "$_\n" foreach @names}} ' 

Вот чистое решение zsh. Это немного подробный, так как нет встроенного способа сохранить повторяющиеся элементы в массиве или в результате результата glob.

 a=(*)(N); a=("${(@io)a}") [[ $#a -le 1 ]] || for i in {2..$#a}; do if [[ ${(L)a[$i]} == ${(L)a[$((i-1))]} ]]; then [[ ${(L)a[$i-2]} == ${(L)a[$((i-1))]} ]] || print -r $a[$((i-1))] print -r $a[$i] fi done 

Без GNU find :

LANG=en_US ls | tr '[AZ]' '[az]' | uniq -c | awk '$1 >= 2 {print $2}'

Я, наконец, справился с этим так:

 find . | tr '[:upper:]' '[:lower:]' | sort | uniq -d 

Я использовал find вместо ls потому что мне нужен полный путь (много подкаталогов). Я не нашел, как это сделать с помощью ls .