Заменить непечатные символы в perl и sed


11

Мне нужно заменить некоторые непечатаемые символы пробелами в файле.

В частности, все символы от 0x00до 0x1F, кроме 0x09(TAB), 0x0A(новая строка), 0x0D(CR)

До сих пор мне просто нужно было заменить 0x00персонажа. Поскольку моя предыдущая ОС была AIX (без команд GNU), я не могу использовать sed(ну, я могу, но у нее были некоторые ограничения). Итак, я нашел следующую команду с помощью perl, которая работала как ожидалось:

perl -p -e 's/\x0/ /g' $FILE_IN > $FILE_OUT 

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

Мои вопросы:

  • Подходит ли эта команда для замены этих символов? Я пытался, и это похоже на работу, но я хочу убедиться:

    perl -p -e 's/[\x00-\x08\x0B\x0C\x0E-\x1F]/ /g' $FILE_IN > $FILE_OUT  
  • Я думал perl -pработает как sed. Итак, почему предыдущая команда работает (по крайней мере, она не дает сбоя), а следующая нет?

    sed -e 's/[\x00-\x08\x0B\x0C\x0E-\x1F]/ /g' $FILE_IN > $FILE_OUT   

    Это говорит мне:

    sed: -e выражение # 1, символ 34: недопустимый символ сопоставления


perl -pпечатает конечный продукт stdinпосле выполнения необходимых операций, в данном случае это просто замена. sedрегулярное выражение может отличаться от perl.
sdkks

Ответы:


11

Это типичная работа для tr:

LC_ALL=C tr '\0-\10\13\14\16-\37' '[ *]' < in > out

В вашем случае это не работает, sedпотому что вы находитесь в локали, где эти диапазоны не имеют смысла. Если вы хотите работать с байтовых значений в отличие от персонажей и где порядок основан на численном значении этих байтов, лучше всего, чтобы использовать C локали . Ваш код работал бы с LC_ALL=CGNU sed, но использование sed(не говоря уже о perl) - это немного излишне (и \xXXони не переносимы между sedреализациями, хотя этот trподход - POSIX).

Вы также можете доверять представлению вашей локали о том, что означают печатные символы:

tr -c '[:print:]\t\r\n' '[ *]'

Но с GNU tr(как это обычно бывает в системах на основе Linux) это работает только в локалях, где символы являются однобайтовыми (как правило, не в UTF-8).

В локали C это также исключает DEL (0x7f) и все байтовые значения выше (не в ASCII).

В локалях UTF-8 вы можете использовать GNU, в sedкоторой нет проблем с GNU tr:

sed 's/[^[:print:]\r\t]/ /g' < in > out

(заметим , что те \r, \tне являются стандартными, и ГНУ sedне распознает их , если POSIXLY_CORRECTв окружающей среде (будет рассматривать их как обратную косую черту, г и т быть частью набора , как требует POSIX)).

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


Я понимаю, что trделает команда. Я понимаю (более или менее), что LC_ALL = Cесть, но не все вместе. Тем не менее tr -dудаляет эти символы, но я хочу заменить пробелами. Извините, название было неправильным. Я только что понял, когда @don_crissti изменился.
Альберт

@ Альберт, прости. Смотрите редактирование и ссылку, которую я добавил.
Стефан Шазелас

Я не уверен в кодировке. Этот файл происходит из среды HOST, в которой используется кодировка EBCDIC, и передается в Linux с использованием XCOM. Например, не ASCII-символы, такие как É, кодируются (используют od -xa) как 0xC9, так что я думаю, что это будет ISO-8859-1.
Альберт

@ Альберт, наверное. Вы можете использовать, locale -aчтобы увидеть, есть ли локали с iso8859-1 в качестве кодировки в вашей системе и использовать LC_CTYPE=<that-locale> tr ...[:print:]...для конвертации непечатаемых в этой локали. Или вы можете использовать iconv для преобразования этих файлов в кодировку вашей локали.
Стефан Шазелас

Я думаю, что это не нужно, потому что кодировка моего языка установлена ​​в LC_ALL=en_US.iso88591. Таким образом, ваша команда ( tr -c '[:print:]\t\r\n' '[ *]') прекрасно работает без изменения локали или преобразования файла. Большое спасибо.
Альберт

0

Я пытался отправить уведомление через libnotify с содержимым, которое может содержать непечатные символы. Существующие решения для меня не совсем работали (использование белого списка символов с использованием trработ, но с удалением любых многобайтовых символов).

Вот что сработало при прохождении теста::

message=$(iconv --from-code=UTF-8 -c <<< "$message")
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.