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


17

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

Ответы:


14

Если у вас есть утилиты 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

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


1
Я просто собирался опубликовать подобное ... Но хуже ответ :)
rozcietrzewiacz

2
Вы действительно нуждаетесь в -mindepth?
rozcietrzewiacz

Я использую Солярис. / Usr / bin / находит того, о ком вы говорите? Я попытался использовать его и дал мне много ошибок.
Lamcro

@lamcro Нет, Solaris не использует GNU find; Я отредактировал ответ, чтобы включить решение без GNU.
Шон Дж. Гофф

Ok. Должен ли я просто вставить его в текстовый файл и дать ему права на выполнение?
Lamcro

12

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

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

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

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

Изменить: Шон Дж. Гофф указал, что это не удастся, если у вас есть имена файлов с символами новой строки. Если вы используете утилиты GNU, вы также можете заставить их работать:

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

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


1
Но посмотрите мой комментарий к ответу Шона Дж. Гоффа, вы можете добавить опцию -print0 для поиска и опцию -z для uniq и sort. Кроме того, вы также хотите -f для сортировки. Тогда это работает. (Я собираюсь отредактировать это в вашем ответе, не стесняйтесь отменить, если вы не одобряете)
Дероберт

Последняя команда дает мне вывод без возврата каретки (результат - все в одной строке). Я использую Red Hat Linux для запуска команды. Первая командная строка работает лучше всего для меня.
вс

2

Сортируйте список имен файлов без учета регистра и печатайте дубликаты. sortимеет возможность сортировки без учета регистра. То же uniqсамое делает GNU , но не другие реализации, и все, что вы можете сделать, 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. Это немного многословно, поскольку нет встроенного способа сохранить дублирующиеся элементы в массиве или глобальном результате.

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

1

Без GNU find:

LANG=en_US ls | tr '[A-Z]' '[a-z]' | uniq -c | awk '$1 >= 2 {print $2}'


2
trявляется очень вероятно , чтобы посеять хаос на любой набор символов , который использует более одного байта на символ. Только первые 256 символов UTF-8 безопасны при использовании tr. Из Википедии (Unix) . Большинство версий tr, включая GNU trи классический Unix tr, работают на ОДНОБАЙТОВ и не совместимы с Юникодом.
Peter.O 19.10.11

1
Обновление до моего предыдущего комментария ... только первые 128 символов UTF-8 являются безопасными. Все символы UTF-8 выше порядкового диапазона 0..127 являются многобайтовыми и могут иметь индивидуальные байтовые значения в других символах. Только байты в диапазоне 0,127 имеют непосредственное отношение к уникальному символу.
Peter.O

Плюс uniqимеет флаг без учета регистра i.
Джейми Китсон

1

Наконец-то мне это удалось:

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

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


2
Оба sortи uniqимеют флажки игнорирования, f и i соответственно.
Джейми Китсон

-1

Для тех, кто хочет затем переименовать и т. Д. Один из файлов:

find . -maxdepth 1 | sort -f | uniq -di | while read f; do echo mv "$f" "${f/.txt/_.txt}"; done
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.