Reemplazar caracteres no imprimibles en perl y sed

11

Necesito reemplazar algunos caracteres no imprimibles con espacios en el archivo.

Específicamente, todos los caracteres de 0x00hasta 0x1F, excepto 0x09(TAB), 0x0A(nueva línea), 0x0D(CR)

Hasta ahora, solo necesitaba reemplazar el 0x00personaje. Como mi sistema operativo anterior era AIX (sin comandos GNU), no puedo usar sed(bueno, puedo pero tenía algunas limitaciones). Entonces, encontré el siguiente comando usando perl, que funcionó como se esperaba:

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

Ahora estoy trabajando en Linux, así que esperaba poder usar el sedcomando.

Mis preguntas:

  • ¿Es este comando apropiado para reemplazar esos caracteres? Lo intenté y parece funcionar, pero quiero asegurarme de que:

    perl -p -e 's/[\x00-\x08\x0B\x0C\x0E-\x1F]/ /g' $FILE_IN > $FILE_OUT  
  • Pensé que perl -pfunciona como sed. Entonces, ¿por qué funciona el comando anterior (al menos, no falla) y el siguiente no?

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

    Me dice:

    sed: -e expresión # 1, char 34: Carácter de intercalación no válido

Albert
fuente
perl -pimprime el producto final de stdindespués de hacer las operaciones que desea, en este caso es solo reemplazo. sedLa expresión regular podría ser diferente a perl.
sdkks

Respuestas:

11

Ese es un trabajo típico para tr:

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

En su caso, no funciona sedporque está en una ubicación donde esos rangos no tienen sentido. Si desea trabajar con valores de bytes en lugar de caracteres y donde el orden se basa en el valor numérico de esos bytes, lo mejor es utilizar la configuración regional C . Su código habría funcionado con LC_ALL=CGNU sed, pero usar sed(y mucho menos perl) es un poco excesivo aquí (y esos \xXXno son portátiles en las sedimplementaciones, mientras que este trenfoque es POSIX).

También puede confiar en la idea de su localidad de los caracteres imprimibles con:

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

Pero con GNU tr(como se encuentra típicamente en los sistemas basados ​​en Linux), eso solo funciona en entornos locales donde los caracteres son de un solo byte (por lo general, no UTF-8).

En la configuración regional de C, eso también excluiría DEL (0x7f) y todos los valores de bytes anteriores (no en ASCII).

En las configuraciones regionales UTF-8, puede usar GNU sedque no tiene el problema que GNU trtiene:

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

(nota que aquellos \r, \tno son estándar, y GNU sedno los reconoce si POSIXLY_CORRECTestá en el medio ambiente (se tratarlos como barra invertida, r y t ser parte del conjunto como requiere POSIX)).

Sin embargo, no convertiría bytes que no forman caracteres válidos si los hubiera.

Stéphane Chazelas
fuente
Entiendo lo que trhace el comando. Yo entiendo (más o menos) lo que LC_ALL = Ces, pero no todos juntos. Sin embargo, tr -delimina esos caracteres, pero quiero reemplazarlos con espacios. Lo siento, el título estaba mal. Me acabo de dar cuenta, cuando @don_crissti modificó.
Albert
@ Albert, lo siento. Vea la edición y el enlace que agregué.
Stéphane Chazelas
No estoy seguro de la codificación. Ese archivo proviene de un entorno HOST, que usa codificación EBCDIC, y se transfiere a Linux usando XCOM. Por ejemplo, los caracteres no ASCII como Ése codifican (usando od -xa) como 0xC9, así que supongo que lo sería ISO-8859-1.
Albert
@ Albert, probablemente. Puede usar locale -apara ver si hay configuraciones regionales con iso8859-1 como el conjunto de caracteres en su sistema y usar LC_CTYPE=<that-locale> tr ...[:print:]...para convertir no imprimibles en esa configuración regional. O puede usar iconv para convertir esos archivos al conjunto de caracteres de su localidad.
Stéphane Chazelas
Creo que no es necesario, porque el juego de caracteres de mi localidad está configurado en LC_ALL=en_US.iso88591. Entonces, su comando ( tr -c '[:print:]\t\r\n' '[ *]') funciona perfectamente sin modificar la configuración regional o la conversión de archivos. Muchas gracias.
Albert
0

Intenté enviar una notificación a través de libnotify, con contenido que puede contener caracteres no imprimibles. Las soluciones existentes no me funcionaron del todo (usando una lista blanca de caracteres usando trWorks, pero elimina cualquier carácter de varios bytes).

Esto es lo que funcionó, mientras pasaba la prueba 💩:

message=$(iconv --from-code=UTF-8 -c <<< "$message")
Todos somos monica
fuente