Eliminar todos los caracteres no ascii de un flujo de trabajo (archivo)


13

¿Cómo eliminaría todos los caracteres que no son ascii de un archivo? ¿Habría un comando específico para realizar esto?

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

Creo que esto encuentra los caracteres dentro del flujo de trabajo, pero ¿cómo eliminaría todas las instancias de los caracteres en cuestión?



2
relacionado: si solo desea evitar problemas con los caracteres de control (en lugar de deshacerse de ellos en silencio), simplemente puede usar cat -vpara mostrarlos en representación ASCII para ellos. (por ejemplo, ^Gpara \007)
Matija Nalis

1
Cuando dice "caracteres no ascii", ¿también incluye caracteres acentuados?
Capitán Man

1
@MatijaNalis Más información sobre la representación: en.wikipedia.org/wiki/Caret_notation
wjandrea

1
¿Cuál es el caso de uso? Muy a menudo hay herramientas específicas o enfoques diferentes que funcionan mucho mejor que simplemente eliminar un grupo de caracteres especiales. Tenga en cuenta que ASCII incluye varios caracteres "especiales" como pestañas verticales, campana y NUL. ¿Está seguro de que no se refiere a caracteres imprimibles ?
l0b0

Respuestas:


26

Los caracteres ASCII son caracteres en el rango de 0 a 177 (octal) inclusive .

Para eliminar caracteres fuera de este rango en un archivo, use

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

El trcomando es una utilidad que funciona en caracteres individuales , ya sea sustituyéndolos con otros caracteres individuales (transliteración), eliminándolos o comprimiendo ejecuciones del mismo carácter en un solo carácter.

El comando anterior leería filey escribiría el contenido modificado newfile. La -dopción trhace que la utilidad elimine caracteres (en lugar de transcribirlos) y -chace que considere caracteres fuera del intervalo dado (en lugar de dentro).

LC_ALL=Cse asegura de que cada valor de byte constituya un carácter válido. Sin ella, algunas trimplementaciones abortarían si encontraran secuencias de bytes que no forman caracteres válidos en la codificación de caracteres de la configuración regional.


Para reemplazar el archivo original con el modificado, use

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

Esto cambia el nombre del nuevo archivo al nombre del archivo anterior después de que se trhaya completado con éxito. Si trno se completa correctamente, ya sea porque no pudo leer el archivo original o no pudo escribir en el nuevo archivo, el archivo original no se modificará.

Alternativamente, para preservar la mayor cantidad posible de los metadatos (permisos, etc.) del archivo original, use

cp file tmpfile &&
LC_ALL=C tr -dc '\0-\177' <tmpfile >file &&
rm tmpfile


9

Si todo lo que necesita es una expresión regular: [\x00-\x7F]que podría aplicar a varias utilidades:

<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'

Comprenda que sed, awk y perl esperan "archivos de texto" como se define en Unix. Todo funciona bien en este caso. Pero específicamente, awk agrega una nueva línea final (ya sea que exista en el archivo fuente o no) (el uso de printf elimina TODAS las nuevas líneas en la entrada). El tr está diseñado para funcionar con cualquier tipo de archivo. Sin embargo, el NUL ( \0) no es un carácter válido en un archivo de texto POSIX y debe evitarse:

Las líneas no contienen caracteres NUL ...

De hecho, muchos caracteres de control generarían otros problemas en algunas condiciones específicas.
Entonces, probablemente necesites[\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'

El rango 7-13 (en decimal) es \a\b\t\n\v\f\r(en orden).
Un rango similar (probablemente más portátil) podría escribirse como [^[:space:][:print:]] (similar because it doesn't include\ a \ b` --bell y retroceso--).

<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:]'

Relacionado:
Regex cualquier carácter ASCII
Solución Perl
Archivo de texto Posix


Tenga en cuenta que la entrada trpuede ser cualquier tipo de archivo, no solo archivos de texto. awkpor otro lado, toma un archivo de texto.
Kusalananda

Es bastante difícil para mí encontrar algo más para llamar a un archivo "solo caracteres ascii", todo menos un "archivo de texto" (sí, sí: en términos simples). @Kusalananda (nota sobre awk añadido de todos modos).
NotAnUnixNazi

Tenga en cuenta que gensub()es una extensión gawk. Desearía gsub(...); print, y usar secuencias octales en lugar de hexadecimales (y LC_ALL = C) para ser (más) portátil.
Stéphane Chazelas

@ StéphaneChazelas ¿Cuál es la limitación de GNU sed que hace que la sintaxis sea específica de GNU? (Entiendo el problema POSIXLY_CORRECT).
NotAnUnixNazi

[^\o0]es hacer coincidir los caracteres que no sean la barra diagonal inversa, o y 0 en POSIX sed(en todas las implementaciones, excepto GNU sed). Esa no es una limitación de GNU sedsino una extensión no compatible, por lo que está deshabilitada cuando POSIXLY_CORRECT está en el entorno).
Stéphane Chazelas
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.