Substitua caracteres não imprimíveis em perl e sed

11

Preciso substituir alguns caracteres não imprimíveis por espaços no arquivo.

Especificamente, todos os caracteres de 0x00até 0x1F, exceto 0x09(TAB), 0x0A(nova linha), 0x0D(CR)

Até agora, eu só precisava substituir o 0x00personagem. Como meu sistema operacional anterior era o AIX (sem comandos GNU), não posso usá-lo sed(bem, posso, mas ele tem algumas limitações). Então, eu encontrei o próximo comando usando perl, que funcionou como esperado:

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

Agora estou trabalhando no Linux, então esperava poder usar o sedcomando.

Minhas perguntas:

  • Este comando é apropriado para substituir esses caracteres? Eu tentei e parece funcionar, mas quero ter certeza:

    perl -p -e 's/[\x00-\x08\x0B\x0C\x0E-\x1F]/ /g' $FILE_IN > $FILE_OUT  
  • Eu pensei que perl -pfunciona como sed. Então, por que o comando anterior funciona (pelo menos, não falha) e o próximo não?

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

    Isso me diz:

    expressão sed: -e # 1, caractere 34: caractere de agrupamento inválido

Albert
fonte
perl -pimprime o produto final stdinapós realizar as operações desejadas, neste caso, é apenas uma substituição. sedA expressão regular de Regex pode ser diferente de perl.
Sdkks

Respostas:

11

Esse é um trabalho típico para tr:

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

No seu caso, não funciona sedporque você está em um local onde esses intervalos não fazem sentido. Se você quiser trabalhar com valores de byte em oposição a personagens e onde a ordem é baseado no valor numérico dos bytes, a sua melhor aposta é a de utilizar o idioma C . Seu código teria funcionado com o LC_ALL=CGNU sed, mas o uso sed(e muito menos perl) é um pouco exagerado aqui (e eles \xXXnão são portáveis ​​em sedimplementações enquanto essa trabordagem é POSIX).

Você também pode confiar na idéia do seu código de idioma sobre o que são os caracteres imprimíveis :

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

Mas com o GNU tr(como normalmente encontrado em sistemas baseados em Linux), isso funciona apenas em locais onde os caracteres são de byte único (normalmente, não UTF-8).

No código de idioma C, isso também excluiria DEL (0x7f) e todos os valores de bytes acima (não no ASCII).

Nas localidades UTF-8, você pode usar o GNU sedque não tem o problema que o GNU trtem:

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

(Note-se que aqueles \r, \tnão são padrão, e GNU sednão irá reconhecê-los se POSIXLY_CORRECTestá no ambiente (se tratá-los como barra invertida, r e t de ser parte do conjunto como POSIX requer)).

Não converteria bytes que não formam caracteres válidos, se houver.

Stéphane Chazelas
fonte
Eu entendo o que o trcomando faz. I compreender (mais ou menos) o que LC_ALL = Cé, mas não todos juntos. No entanto, tr -dremove esses caracteres, mas quero substituir por espaços. Desculpe, o título estava errado. Acabei de perceber quando o @don_crissti foi modificado.
Albert
@ Albert, desculpe. Veja a edição e o link que adicionei.
Stéphane Chazelas
Não tenho certeza sobre a codificação. Esse arquivo vem de um ambiente HOST, que usa codificação EBCDIC, e é transferido para o Linux usando XCOM. Por exemplo, caracteres não ASCII como Ésão codificados (usando od -xa) como 0xC9, então eu acho que seria ISO-8859-1.
611 Albert
@ Albert, provavelmente. Você pode usar locale -apara ver se há códigos de idioma com iso8859-1 como o conjunto de caracteres em seu sistema e usar LC_CTYPE=<that-locale> tr ...[:print:]...para converter não imprimíveis nesse código de idioma. Ou você pode usar o iconv para converter esses arquivos no conjunto de caracteres do código de idioma.
Stéphane Chazelas
Eu acho que não é necessário, porque o conjunto de caracteres do meu código de idioma está definido como LC_ALL=en_US.iso88591. Portanto, seu command ( tr -c '[:print:]\t\r\n' '[ *]') funciona perfeitamente sem modificar a localidade ou converter o arquivo. Muito obrigado.
611 Albert
0

Eu estava tentando enviar uma notificação via libnotify, com conteúdo que pode conter caracteres não imprimíveis. As soluções existentes não funcionaram muito bem para mim (usando uma lista de desbloqueio de caracteres usando trobras, mas retira qualquer caractere de vários bytes).

Aqui está o que funcionou, ao passar no teste::

message=$(iconv --from-code=UTF-8 -c <<< "$message")
Somos todos Monica
fonte