Поиск и удаление файлов с именами, отличными от ascii

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

Пример:

ls -l
-rwxrwxr-x 1 cws cws      0 Dec 28  2011 ??"??

ls -lb
-rwxrwxr-x 1 cws cws      0 Dec 28  2011 \a\211"\206\351

Я хотел бы найти все такие файлы.

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

enter image description here

Я хочу найти эти файлы с непечатаемыми символами и просто удалить их.

Ответ 1

Символы, отличные от ASCII

Коды символов ASCII варьируются от 0x00 до 0x7F в шестнадцатеричном формате. Поэтому любой символ с кодом, большим, чем 0x7F, является символом, отличным от ASCII. Это включает в себя основную часть символов в UTF-8 (коды ASCII по существу являются подмножеством UTF-8). Например, японский символ

кодируется в шестнадцатеричном формате в UTF-8 как

E3 81 82

UTF-8 был кодировкой символов по умолчанию, среди прочих, Red Hat Linux с версии 8.0 (2002), SuSE Linux с версии 9.1 (2004) ) и Ubuntu Linux с версии 5.04 (2005).

Управляющие символы ASCII

Из кодов ASCII 0x00 через 0x1F и 0x7F отображаются управляющие символы, такие как ESC (0x1B). Эти управляющие символы изначально не предназначались для печати, хотя некоторые из них, такие как символ строки строки 0x0A, могут быть интерпретированы и отображены.

В моей системе ls по умолчанию отображаются все управляющие символы как ?, если я не передаю параметр --show-control-chars. Я предполагаю, что файлы, которые вы хотите удалить, содержат управляющие символы ASCII, в отличие от символов, отличных от ASCII. Это важное различие: если вы удаляете имена файлов, содержащие символы, отличные от ASCII, вы можете сдуть законные файлы, которые просто называются на другом языке.

Регулярные выражения для кодов символов

POSIX

POSIX предоставляет очень удобную коллекцию классов символов для работы с этими типами символов (благодаря bashophil для указания этого):

[:cntrl:] Control characters
[:graph:] Graphic printable characters (same as [:print:] minus the space character)
[:print:] Printable characters (same as [:graph:] plus the space character)

PCRE

Совместимые регулярные выражения Perl позволяют использовать шестнадцатеричные коды символов с использованием синтаксиса

\x00

Например, регулярное выражение PCRE для японского символа будет

\xE3\x81\x82

В дополнение к перечисленным выше классам символов POSIX, PCRE также предоставляет класс символов [:ascii:], который является удобным сокращением для [\x00-\x7F].

Версия GNU grep поддерживает PCRE с использованием флага -P, но BSD grep (например, в Mac OS X) не работает. Ни GNU, ни BSD find не поддерживают регулярные выражения PCRE.

Поиск файлов

GNU find поддерживает POSIX-регексы (благодаря iscfrc для указания чистого решения find, чтобы избежать появления дополнительных процессов). Следующая команда перечисляет все имена файлов (но не имена каталогов) под текущим каталогом, который содержит непечатаемые управляющие символы:

find -type f -regextype posix-basic -regex '^.*/[^/]*[[:cntrl:]][^/]*$'

Регулярное выражение немного сложнее, потому что параметр -regex должен соответствовать всему пути к файлу, а не только имени файла, и потому что я предполагаю, что мы не хотим сбрасывать файлы с нормальными именами просто потому, что они находятся внутри каталогов с именами, содержащими контрольные символы.

Чтобы удалить соответствующие файлы, просто передайте параметр -delete в find, после всех остальных параметров (это важно, переход -delete, поскольку первый параметр сдует все в вашем текущем каталоге):

find -type f -regextype posix-basic -regex '^.*/[^/]*[[:cntrl:]][^/]*$' -delete

I высоко рекомендуется сначала запустить команду без -delete, чтобы вы могли видеть, что будет удалено до ее слишком позднего времени.

Если вы также передаете параметр -print, вы можете увидеть, что удаляется при запуске команды:

find -type f -regextype posix-basic -regex '^.*/[^/]*[[:cntrl:]][^/]*$' -print -delete

Чтобы сдуть любые пути (файлы или каталоги), содержащие контрольные символы, можно упростить регулярное выражение, и вы можете отказаться от опции -type:

find -regextype posix-basic -regex '.*[[:cntrl:]].*' -print -delete

С помощью этой команды, если имя каталога содержит управляющие символы, даже если ни один из имен файлов внутри каталога не будет выполнен, все они будут удалены.


Обновление: поиск как не-ASCII, так и управляющих символов

Похоже, что ваши файлы содержат как символы, не содержащие ASCII, так и управляющие символы ASCII. Как оказалось, [:ascii:] не является символьным классом POSIX, но он предоставляется PCRE. Я не мог найти POSIX-регулярное выражение, чтобы это сделать, поэтому Perl на помощь. Мы по-прежнему будем использовать find для перемещения по дереву каталогов, но мы передадим результаты Perl для обработки.

Чтобы убедиться, что мы можем обрабатывать имена файлов, содержащие новые строки (что представляется вероятным в этом случае), нам нужно использовать аргумент -print0 для find (поддерживается как в GNU, так и в версиях BSD); это разделяет записи с нулевым символом (0x00) вместо новой строки, поскольку нулевой символ является единственным символом, который не может быть в допустимом имени файла в Linux. Нам нужно передать соответствующий флаг -0 нашему Perl-коду, чтобы он знал, как разделяются записи. Следующая команда будет печатать каждый путь внутри текущего каталога, рекурсивно:

find . -print0 | perl -n0e 'print $_, "\n"'

Обратите внимание, что эта команда генерирует только один экземпляр интерпретатора Perl, что хорошо для производительности. Аргумент пустого пути (в данном случае . для CWD) является необязательным в GNU find, но требуется в BSD find в Mac OS X, поэтому я включил его для переносимости.

Теперь для нашего регулярного выражения. Ниже приведены имена соответствия регулярных выражений PCRE, которые содержат либо символы, не содержащие ASCII, либо непечатаемые (т.е. управляющие) символы (или оба):

[[:^ascii:][:cntrl:]]

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

find . -print0 | perl -n0e 'chomp; print $_, "\n" if /[[:^ascii:][:cntrl:]]/'

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

find . -print0 | perl -MFile::Path=remove_tree -n0e 'chomp; remove_tree($_, {verbose=>1}) if /[[:^ascii:][:cntrl:]]/'

Это также будет распечатывать то, что удаляется при запуске команды (хотя управляющие символы интерпретируются так, что результат не будет соответствовать выходному сигналу ls).

Ответ 2

Теперь вы, вероятно, решили свой вопрос, но это не сработало для моего случая, поскольку у меня были файлы, которые не показывались find, когда я использовал переключатель -regex. Поэтому я разработал это обходное решение, используя ls. Надеюсь, что это может быть полезно кому-то.

В основном, то, что сработало для меня, было следующим:

ls -1 -R -i | grep -a "[^A-Za-z0-9_.':@ /-]" | while read f; do inode=$(echo "$f" | cut -d ' ' -f 1); find -inum "$inode" -delete; done

Ломать его по частям:

ls -1 -R -i

Это будет рекурсивно (-R) список (ls) файлов в текущем каталоге, по одному файлу в строке (-1), префикс каждого файла по номеру inode (-i). Результаты будут переданы по каналам на grep.

grep -a "[^A-Za-z0-9_.':@ /-]"

Отфильтруйте каждую запись, рассматривая каждый ввод как текст (-a), даже если он в конечном итоге является двоичным. grep будет пропускать строку, если она содержит символ, отличный от указанного в списке. Результаты будут переданы по каналам на while.

while read f
do
    inode=$(echo "$f" | cut -d ' ' -f 1)
    find -inum "$inode" -delete
done

Этот while будет перебирать все записи, извлекая номер inode и передавая inode на find, который затем удалит файл.

Ответ 3

Вы можете печатать только строки, содержащие обратную косую черту grep:

ls -lb | grep \\\\

Ответ 4

Можно использовать PCRE с grep -P, просто не с помощью find (к сожалению). Вы можете найти цепочку grep с помощью exec. С помощью PCRE (perl regex) мы можем использовать класс ascii и найти любой char, который не является ascii.

find . -type f -exec sh -c "echo \"{}\" | grep -qP '[^[:ascii:]]'" \; -exec rm {} \;

Следующий exec не будет выполняться, если первый не возвращает код без ошибок. В этом случае это означает, что выражение соответствует имени файла. Я использовал sh -c, потому что -exec не любит трубы.

Ответ 5

Основываясь на этом ответе, попробуйте:

LC_ALL=C find . -regex '.*[^ -~].*' -print # -delete

или

LC_ALL=C find . -type f -regex '*[^[:alnum:][:punct:]]*' -print # -delete

Примечание. После того, как файлы напечатаны правильно, удалите символ #.

Смотрите также: Как grep для всех символов, отличных от ASCII.