GNU/Linux >> Tutoriales Linux >  >> Linux

Cómo hacer grep para Unicode en un script bash

grep es la herramienta incorrecta para el trabajo.

Ves el � U+FFFD REPLACEMENT CHARACTER no porque esté literalmente en el contenido del archivo, sino porque miró un archivo binario con una herramienta que se supone que solo maneja entradas basadas en texto. La forma estándar de manejar entradas no válidas (es decir, datos binarios aleatorios) es reemplazar todo lo que no es válido en la configuración regional actual (probablemente UTF-8) con U+FFFD antes de que llegue a la pantalla.

Eso significa que es muy probable que un literal \xEF\xBF\xBD (la secuencia de bytes UTF-8 para el carácter U+FFFD) nunca aparece en el archivo. grep tiene toda la razón al decirte que no hay ninguno.

Una forma de detectar si un archivo contiene algún binario desconocido es con el file(1) comando:

$ head -c 100 /dev/urandom > rubbish.bin
$ file rubbish.bin
rubbish.bin: data

Para cualquier tipo de archivo desconocido, simplemente dirá data . Prueba

$ file out.txt | grep '^out.txt: data$'

para verificar si el archivo realmente contiene algún binario arbitrario y, por lo tanto, probablemente basura.

Si quiere asegurarse de que out.txt es solo un archivo de texto codificado en UTF-8, también puede usar iconv :

$ iconv -f utf-8 -t utf-16 out.txt >/dev/null

TL;DR:

grep -axv '.*' out.txt 

respuesta larga

Ambas respuestas actuales son extremadamente engañosas y básicamente incorrectas.

Para probar, obtenga estos dos archivos (de un desarrollador muy respetado:Markus Kuhn):

$ wget https://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-demo.txt
$ wget https://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt

Demostración

El primer UTF-8-demo.txt es un archivo diseñado para mostrar lo bien que UTF-8 es capaz de presentar muchos idiomas, matemáticas, braille y muchos otros tipos de caracteres útiles. Eche un vistazo con un editor de texto (que entienda utf-8) y verá muchos ejemplos y no .

La prueba que propone una respuesta:limitar el rango de caracteres a \x00-\x7F rechazará casi todo dentro de este archivo.
Eso está muy mal y no eliminará ningún ya que no hay ninguno en ese archivo .

El uso de la prueba recomendada en esa respuesta eliminará 72.5 % del archivo:

$ grep -oP "[^\x00-\x7F]" UTF-8-demo.txt | tr -d '\n' | wc -c
10192
$ cat UTF-8-demo.txt | wc -c
14058

Eso es (para la mayoría de los propósitos prácticos) el archivo completo. Un archivo muy bien diseñado para mostrar caracteres perfectamente válidos.

Prueba

El segundo archivo está diseñado para probar varios casos límite para confirmar que los lectores utf-8 están haciendo un buen trabajo. Contiene dentro muchos caracteres que harán que se muestre un '�'. Pero la otra recomendación de respuesta (la seleccionada) para usar file falla groseramente con este archivo. Solo eliminando un byte cero (\0 ) (que técnicamente es ASCII válido) y un \x7f byte (DEL - eliminar) (que claramente también es un carácter ASCII) hará que todos el archivo válido para el file comando:

$ cat UTF-8-test.txt | tr -d '\0\177' > a.txt
$ file a.txt 
a.txt: Non-ISO extended-ASCII text, with LF, NEL line terminators

No solo file fallan al detectar los muchos caracteres incorrectos, pero también fallan al detectar e informar que es un archivo codificado en UTF-8.

Y sí, file es capaz de detectar y reportar texto codificado en UTF-8:

$ echo "ééakjfhhjhfakjfhfhaéá" | file -
/dev/stdin: UTF-8 Unicode text

Además, file no informa como ASCII la mayoría de los caracteres de control en el rango de 1 a 31. (file ) reporta algunos rangos como data :

$ printf '%b' "$(printf '\\U%x' {1..6})" | file -
/dev/stdin: data

Otros como ASCII text :

$ printf '%b' "$(printf '\\U%x' 7 {9..12})" | file -
/dev/stdin: ASCII text

Como rango de caracteres imprimibles (con líneas nuevas):

$ printf '%b' "$(printf '\\U%x' {32..126} 10)" | file -
/dev/stdin: ASCII text

Pero algunos rangos pueden causar resultados extraños:

$ printf '%b' "$(printf '\\U%x' {14..26})" | file -
/dev/stdin: Atari MSA archive data, 4113 sectors per track, starting track: 5141, ending track: 5655

El programa file no es una herramienta para detectar texto, sino para detectar magia números en programas o archivos ejecutables.

Los rangos file detect, y el tipo correspondiente informado que encontré fue:

  • Valores de un byte, en su mayoría ascii:

    {1..6} {14..26} {28..31} 127   :data
    {128..132} {134..159}          :Non-ISO extended-ASCII text
    133                            :ASCII text, with LF, NEL line terminators
    27                             :ASCII text, with escape sequences
    13                             :ASCII text, with CR, LF line terminators
    8                              :ASCII text, with overstriking
    7 {9..12} {32..126}            :ASCII text
    {160..255}                     :ISO-8859 text
    
  • Rangos codificados en Utf-8:

    {1..6} {14..26} {28..31} 127   :data
    27                             :ASCII text, with escape sequences
    13                             :ASCII text, with CR, LF line terminators
    8                              :ASCII text, with overstriking
    7 {9..12} {32..126}            :ASCII text
    {128..132} {134..159}          :UTF-8 Unicode text
    133                            :UTF-8 Unicode text, with LF, NEL line terminators
    {160..255}                     :UTF-8 Unicode text
    {256..5120}                    :UTF-8 Unicode text
    

Una posible solución se encuentra a continuación.

Respuesta anterior.

El valor Unicode para el carácter que está publicando es:

$ printf '%x\n' "'�"
fffd

Sí, ese es un carácter Unicode 'CARÁCTER DE REEMPLAZO' (U+FFFD). Ese es un carácter que se usa para reemplazar cualquier inválido Carácter Unicode encontrado en el texto. Es una "ayuda visual", no un personaje real. Para buscar y enumerar todas las líneas completas que contienen UNICODE no válidos uso de caracteres:

grep -axv '.*' out.txt 

pero si solo desea detectar si algún carácter no es válido, use:

grep -qaxv '.*' out.txt; echo $?

Si el resultado es 1 el archivo está limpio, de lo contrario será cero 0 .

Si lo que preguntabas era:cómo encontrar el personaje, entonces, usa esto:

➤ a='Basically, if the file "out.txt" contains "�" anywhere in the file I'
➤ echo "$a" | grep -oP $(printf %b \\Ufffd)
�

O si su sistema procesa correctamente el texto UTF-8, simplemente:

➤ echo "$a" | grep -oP '�'
�

Esta respuesta muy temprana fue para la publicación original que era:

Cómo hacer grep para Unicode en un script bash

if grep -q "�" out.txt
    then
        echo "working"
    else
        cat out.txt  fi

Básicamente, si el archivo "out.txt" contiene "�" en cualquier parte del archivo, me gustaría que repita "trabajando" Y si el archivo "out.txt" NO contiene "�" en ninguna parte del archivo, entonces me gustaría para gato fuera.txt

Prueba

grep -oP "[^\x00-\x7F]"

con un if .. then declaración de la siguiente manera:

if grep -oP "[^\x00-\x7F]" file.txt; then
    echo "grep found something ..."
else
    echo "Nothing found!"
fi

Explicación:

  • -P , --perl-regexp :PATRÓN es una expresión regular de Perl
  • -o , --only-matching :muestra solo la parte de una línea que coincide con el PATRÓN
  • [^\x00-\x7F] es una expresión regular para coincidir con un solo carácter no ASCII.
  • [[:ascii:]] - coincide con un único carácter ASCII
  • [^[:ascii:]] - coincide con un único carácter no ASCII

en bash

LC_COLLATE=C grep -o '[^ -~]' file

Linux
  1. ¿Cómo depurar un script Bash?

  2. ¿Cómo verificar la subcadena en Shell Script Bash?

  3. Cómo hacer que el comando de alias funcione en script bash o archivo bashrc

  4. ¿Cómo puedo buscar un patrón de varias líneas en un archivo?

  5. ¿Cómo sé el nombre del archivo de script en un script Bash?

Cómo leer un archivo línea por línea en Bash

35 ejemplos de secuencias de comandos Bash

Cómo ejecutar un script Bash

Cómo grep \n en el archivo

Ver puntos de código Unicode para todas las letras en el archivo en bash

Cómo grep una sección de un archivo en bash Shell