идентифицировать файлы с не-ASCII или непечатаемыми символами в имени файла

24

В каталоге размером 80 ГБ с приблизительно 700 000 файлов в имени файла есть имена файлов с неанглийскими символами. Помимо трудоемкого просмотра списка файлов:

  • Простой способ перечислить или иным образом идентифицировать эти имена файлов?
  • Способ создания печатных символов не на английском языке - тех символов, которые не перечислены в диапазоне для печати man ascii(чтобы я мог проверить, что эти файлы идентифицируются)?
suspectus
источник

Ответы:

32

Предполагая, что «иностранный» означает «не символ ASCII», вы можете использовать findс шаблоном, чтобы найти все файлы, не имеющие печатаемых символов ASCII в их именах:

LC_ALL=C find . -name '*[! -~]*'

(Пробел - первый печатный символ, указанный на http://www.asciitable.com/ , ~- последний.)

LC_ALL=CТребуется подсказка для (собственно, LC_CTYPE=Cи LC_COLLATE=C), иначе диапазон символов интерпретируется неправильно. Смотрите также страницу руководства glob(7). Так как LC_ALL=Cвызывает findинтерпретацию строк как ASCII, он будет печатать многобайтовые символы (такие как π) как вопросительные знаки. Чтобы это исправить, направьте в какую-нибудь программу (например cat) или перенаправьте в файл.

Вместо указания диапазонов символов [:print:]также можно использовать для выбора «печатных символов». Обязательно установите локаль C, иначе вы получите (на первый взгляд) произвольное поведение.

Пример:

$ touch $(printf '\u03c0') "$(printf 'x\ty')"
$ ls -F
dir/  foo  foo.c  xrestop-0.4/  xrestop-0.4.tar.gz  π
$ find -name '*[! -~]*'       # this is broken (LC_COLLATE=en_US.UTF-8)
./x?y
./dir
./π
... (a lot more)
./foo.c
$ LC_ALL=C find . -name '*[! -~]*'
./x?y
./??
$ LC_ALL=C find . -name '*[! -~]*' | cat
./x y
./π
$ LC_ALL=C find . -name '*[![:print:]]*' | cat
./x y
./π
Lekensteyn
источник
1
Имейте в виду, что у вас есть имена файлов, которые используют внешние наборы символов, которые несовместимы с UTF-8 или ASCII. В этих случаях вы можете увидеть знаки вопроса вместо символов.
Лекенштейн
1
+1, но я бы использовал LC_ALL=Cвместо, LC_COLLATE=Cтак как не имеет смысла устанавливать LC_COLLATE в C без установки LC_CTYPEи чтобы убедиться, что он все еще работает, даже когда переменная LC_ALL находится в среде.
Стефан Шазелас
Если SPCэто версия для печати , то , что о TABи LFкоторые также обычно находятся в текстовых файлах?
Стефан Шазелас
1
Спасибо - найдено шесть файлов с длинным дефисом, коротким дефисом и вариантом одинарной кавычки. Все это произошло от MS Word. Нет разницы в файлах, перечисленных между LC_ALL и LC_COLLATE. LC_COLLATE правильно отображал не-ASCII символы, тогда как LC_ALL отображал ??? вместо. Отличный ответ!
Подозреваемый
1
@suspectus Я обновил ответ на основе предложений от Стефана. Для LC_COLLATEи LC_CTYPEсм. Также find(1)справочную страницу.
Лекенштейн
6

Если вы переводите каждое имя файла с помощью tr -d '[\200-\377]'и сравниваете его с исходным именем, то любые имена файлов, содержащие специальные символы, не будут одинаковыми.

(Выше, предполагая, что вы имеете в виду не ASCII с иностранным)

Timo
источник
2
Это также удаляет [и ]в большинстве trреализаций.
Стефан Шазелас
Да - это действительно удалить [и ]в моей системе.
Подозреваемый
+1 - решение нашло все (шесть) имен файлов с не ASCII-символами (в дополнение к [и ]s). Спасибо.
Подозреваемый
3

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

find . -type f > filenames
while read filename; do
      stripped="$(printf '%s\n' "$filename" | tr -d -C '[[:alnum:]][[:space:]][[:punct:]]')"
      test "$filename" = "$stripped" || printf '%s\n' "$filename"; 
done < filenames
Эрнест А
источник
4
это хорошее продолжение моего ответа, но оно слишком простое, в именах файлов могут быть новые строки, и тогда ваш скрипт не будет работать
Timo
1
Если вы хотите обработать findвывод, используйте вывод / ввод с NUL-завершением, как показано в этом ответе .
Лекенштейн
0

Принятый ответ полезен, но если ваши имена файлов уже в кодировке, указанной в LANG/ LC_CTYPE, лучше просто сделать:

LC_COLLATE=C find . -name '*[! -~]*'

На классы символов влияет LC_CTYPE, но приведенная выше команда не использует классы символов, а только диапазоны, поэтому LC_CTYPEпросто предотвращает замену необычных символов на вопросительные знаки.

Самба
источник