Удаление всех не-ascii символов из рабочего процесса (файла)

13

Как удалить все символы, не относящиеся к ascii, из одного файла? Будет ли определенная команда для выполнения этого?

grep --colour='auto' -P -n'[^\x00-\x7]' /usr/local/...

Я считаю, что это находит символы в рабочем процессе, но как бы я удалил все экземпляры рассматриваемых символов?

Мизоле Ни
источник
2
related: если вы просто хотите избежать проблем с контрольными символами (вместо того, чтобы молча избавляться от них), вы можете просто использовать их cat -vдля отображения в репрезентации ASCII для них. (например, ^Gдля \007)
Матия Налис
1
Когда вы говорите «не-символы ascii», вы также включаете акцентированные символы?
Капитан Мэн
1
@MatijaNalis Больше информации о представительстве: en.wikipedia.org/wiki/Caret_notation
wjandrea
1
Какой вариант использования? Очень часто существуют специальные инструменты или другие подходы, которые работают намного лучше, чем просто удаление набора специальных символов. Обратите внимание, что в ASCII есть несколько «специальных» символов, таких как вертикальные табуляции, колокольчик и NUL. Вы уверены, что не имеете в виду печатные символы?
10

Ответы:

26

Символы ASCII - это символы в диапазоне от 0 до 177 (восьмеричные) включительно .

Чтобы удалить символы вне этого диапазона в файле, используйте

LC_ALL=C tr -dc '\0-\177' <file >newfile

Команда tr- это утилита, которая работает с отдельными символами , либо заменяя их другими отдельными символами (транслитерация), удаляя их, либо сжимая серии одного и того же символа в один символ.

Команда выше будет читать fileи записывать измененный контент в newfile. -dОпция trделает утилиту удаления символов (вместо транслитерации их), и -cделает его рассматривать символы вне заданного интервала (вместо внутреннего).

LC_ALL=Cгарантирует, что каждое значение байта составляет допустимый символ. Без этого некоторые trреализации прервались бы, если бы они нашли последовательности байтов, которые не формируют допустимые символы в кодировке символов локали.


Чтобы заменить исходный файл на измененный, используйте

LC_ALL=C tr -dc '\0-\177' <file >newfile &&
mv newfile file

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

В качестве альтернативы, чтобы сохранить как можно больше метаданных (разрешений и т. Д.) Исходного файла, используйте

cp file tmpfile &&
LC_ALL=C tr -dc '\0-\177' <tmpfile >file &&
rm tmpfile
Кусалананда
источник
9

Если все, что вам нужно, это регулярное выражение: [\x00-\x7F]вы можете обратиться к нескольким утилитам:

<file LC_ALL=C   sed   's/[^\o0-\o177]//g'      # GNU sed without POSIXLY_CORRECT
<file LC_ALL=C   awk   '{gsub(/[^\0-\177]/,"");print}'
<file            perl  -pe 's/[^[:ascii:]]//g;'
<file LC_ALL=C   tr    -dc '\0-\177'

Поймите, что sed, awk и perl ожидают «текстовые файлы», как определено в Unix. Все хорошо работает в этом случае. Но, в частности, awk добавляет завершающую новую строку (существует ли она в исходном файле или нет) (использование printf удаляет ВСЕ новые строки на входе). Тр предназначен для работы с любым типом файлов. Однако NUL ( \0) не является допустимым символом в текстовом файле POSIX, и его следует избегать:

Строки не содержат символов NUL ...

Фактически, многие управляющие символы могут создавать другие проблемы при определенных условиях.
Так что, вероятно, вам нужно[\x07-\x0d\x20-\x7e]

<file LC_ALL=C   sed   's/[^\o007-\o015\o040-\o176]//g'            # GNU sed without POSIXLY_CORRECT
<file LC_ALL=C   awk   '{gsub(/[^\0-\15\40-\176]/,"");print}'
<file            perl  -pe 's/[^\x{7}-\x{d}\x{20}-\x{7e}]//g;'
<file LC_ALL=C   tr    -dc '\7-\15\40-\176'

Диапазон 7-13 (в десятичном формате) является \a\b\t\n\v\f\r(по порядку).
Подобный (возможно, более переносимый) диапазон может быть записан как [^[:space:][:print:]] (similar because it doesn't include\ a \ b` --bell и backspace--).

<file LC_ALL=C   sed   's/[^[:space:][:print:]]//g'  # GNU sed without POSIXLY_CORRECT
<file LC_ALL=C   awk   '{gsub(/[^[:space:][:print:]]/,"");print}'
<file            perl   -pe 's/[^[:space:][:print:]]//g;'
<file LC_ALL=C   tr     -dc '[:space:][:print:]'

Связанный: регулярное выражение
любого символа ASCII
Perl решение
Текстовый файл Posix

NotAnUnixNazi
источник
Обратите внимание, что входными данными trмогут быть файлы любого типа, а не только текстовые файлы. awkс другой стороны, принимает текстовый файл.
Кусалананда
Мне довольно сложно найти что-то еще, чтобы назвать файл «только символами ascii», что угодно, кроме «текстового файла» (да, да: в терминах непрофессионала). @Kusalananda (заметка о awk добавлена ​​в любом случае).
NotAnUnixNazi
Обратите внимание, что gensub()это расширение gawk. Вы хотели бы gsub(...); print, и использовать восьмеричные вместо шестнадцатеричных последовательностей (и LC_ALL = C), чтобы быть (более) переносимым.
Стефан
@ StéphaneChazelas Какое ограничение GNU sed делает синтаксис GNU специфичным (я понимаю проблему POSIXLY_CORRECT).
NotAnUnixNazi
[^\o0]должен совпадать с символами, отличными от обратной косой черты, o и 0 в POSIX sed(во всех реализациях, кроме GNU sed). Это не ограничение GNU, sedа несовместимое расширение, поэтому оно отключено, когда POSIXLY_CORRECT находится в среде).
Стефан