ідентифікуйте файли з символами, що не належать до ASCII, або у файлі, що не друкуються


24

У каталозі розміром 80 ГБ з приблизно 700 000 файлів є деякі імена файлів з неанглійськими символами у назві файлу. Окрім того, що копітко перебирається через список файлів, є:

  • Простий спосіб перелічити або іншим чином визначити ці імена файлів?
  • Спосіб генерації символів, які можна друкувати не англійською мовою - ті символи, які не вказані в діапазоні для друку man ascii(щоб я міг перевірити, чи ці файли ідентифікуються)?

Відповіді:


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
./π

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 з іноземними)


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
це приємне розширення моєї відповіді, але це занадто просто, імена файлів можуть містити в них нові рядки, і тоді ваш сценарій не буде працювати
Тимо

1
Якщо ви хочете findвивести після обробки процес , використовуйте вихід / вхід, що закінчується NUL, як показано в цій відповіді .
Лекенштейн

0

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

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

Класи символів впливають на LC_CTYPE, але наведена вище команда не використовує класи символів, а лише діапазони, тому LC_CTYPEпросто не дозволяє замінювати незвичні символи знаками запитання.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.