¿Cómo hago grep para todos los caracteres no ASCII?

359

Tengo varios archivos XML muy grandes y estoy tratando de encontrar las líneas que contienen caracteres no ASCII. He intentado lo siguiente:

grep -e "[\x{00FF}-\x{FFFF}]" file.xml

Pero esto devuelve todas las líneas del archivo, independientemente de si la línea contiene un carácter en el rango especificado.

¿Tengo la sintaxis incorrecta o estoy haciendo algo más mal? También he intentado:

egrep "[\x{00FF}-\x{FFFF}]" file.xml 

(con comillas simples y dobles que rodean el patrón).

pconrey
fuente
Los caracteres ASCII tienen solo un byte de longitud, por lo que, a menos que el archivo sea unicode, no debe haber caracteres por encima de 0xFF.
zdav
¿Cómo vamos por encima de \ xFF? Grep da el error "grep: rango fuera de orden en la clase de caracteres".
Mudit Jain

Respuestas:

495

Puedes usar el comando:

grep --color='auto' -P -n "[\x80-\xFF]" file.xml

Esto le dará el número de línea y resaltará los caracteres no ascii en rojo.

En algunos sistemas, dependiendo de su configuración, lo anterior no funcionará, por lo que puede grep por el inverso

grep --color='auto' -P -n "[^\x00-\x7F]" file.xml

Tenga en cuenta también que el bit importante es la -Pbandera que equivale a --perl-regexp: por lo que interpretará su patrón como una expresión regular de Perl. También dice que

esto es altamente experimental y grep -P puede advertir sobre características no implementadas.

jerrymouse
fuente
42
Esto no funcionará en BSD grep(en OS X 10.8 Mountain Lion), ya que no admite la Popción.
Bastiaan M. van de Weerd
20
Para actualizar mi último comentario, la versión GNU de grepestá disponible en la dupesbiblioteca de Homebrew (habilitar usando brew tap homebrew/dupes):brew install grep
Bastiaan M. van de Weerd
48
@BastiaanVanDeWeerd es correcto, grep en OSX 10.8 ya no es compatible con PCRE ("expresiones regulares compatibles con Perl") ya que Darwin ahora usa BSD grep en lugar de GNU grep. Una alternativa a la instalación de la dupesbiblioteca es instalar en su pcrelugar: brew install pcre... como parte de esto, obtendrá la pcregreputilidad, que puede usar de la siguiente manera:pcregrep --color='auto' -n "[\x80-\xFF]" file.xml
pvandenberk
15
Para brewusuarios de Mac , se pueden instalar los coreutils de GNUbrew install coreutils . Esto le dará muchas herramientas GNU con el prefijo 'g', en este caso, use ggrep. Esto debería evitar los problemas derivados de la sustitución de una utilidad del sistema, ya que los scripts Mac específicos del sistema ahora dependen de BSD grep.
Joel Purra
22
esto funciona bien en una Mac ag "[\x80-\xFF]" fileque solo necesita instalarthe_silver_searcher
slf
123

En lugar de hacer suposiciones sobre el rango de bytes de caracteres no ASCII, como lo hacen la mayoría de las soluciones anteriores, es un poco mejor IMO ser explícito sobre el rango de bytes real de caracteres ASCII.

Entonces, la primera solución, por ejemplo, sería:

grep --color='auto' -P -n '[^\x00-\x7F]' file.xml

(que básicamente greps para cualquier carácter fuera del rango hexadecimal ASCII: desde \ x00 hasta \ x7F)

En Mountain Lion eso no funcionará (debido a la falta de soporte PCRE en BSD grep) , pero con pcreinstalado a través de Homebrew, lo siguiente funcionará igual de bien:

pcregrep --color='auto' -n '[^\x00-\x7F]' file.xml

¿Algún pros o contras que alguien pueda pensar?

pvandenberk
fuente
99
Esto realmente funcionó para mí donde fallaron las soluciones anteriores. ¡Encontrar apóstrofes de M $ Word no ha sido más fácil!
AlbertEngelB
2
Si tiene un shell compatible con bash pero no funciona pcre-grep, LC_COLLATE=C grep $'[^\1-\177]'funciona (para archivos sin bytes nulos)
idupree
2
Esta solución parece funcionar de manera más consistente que las anteriores.
0xcaff
1
Tuve que usar esto para recoger Kanji, cirílico y chino tradicional en mi archivo UTF8, usando "[\ x80- \ xFF]" se perdió todo esto.
buckaroo1177125
1
La ventaja es que esto funcionó excelentemente, mientras que las otras opciones eran geniales, pero no tanto. No hay contras encontrados hasta ahora.
jwpfox
67

Lo siguiente funciona para mí:

grep -P "[\x80-\xFF]" file.xml

Los caracteres no ASCII comienzan en 0x80 y van a 0xFF cuando se miran los bytes. Grep (y su familia) no realizan el procesamiento Unicode para fusionar caracteres de varios bytes en una sola entidad para la coincidencia de expresiones regulares como parece querer. La -Popción en mi grep permite el uso de \xddescapes en las clases de personajes para lograr lo que quieres.

Thelema
fuente
1
Para la vista que puede no saber inmediatamente cómo llamar a esto a través de múltiples archivos, simplemente ejecute: find. -name * .xml | xargs grep -P "[\ x80- \ xFF]"
David Mohundro
1
Esto devuelve una coincidencia, pero no hay indicación de qué es el personaje y dónde está. ¿Cómo se ve cuál es el personaje y dónde está?
Faheem Mitha
Agregar "-n" dará el número de línea, además los caracteres no visibles se mostrarán como un bloque en la terminal: grep -n -P "[\ x80- \ xFF]"
file.xml
44
Tengo un problema con Hangul Korean: echo '소녀시대' | grep -P "[\x80-\xFF]"no me devuelve nada, ¿alguien más puede confirmarlo? (GNU grep 2.21)
frabjous
@frabjous Lo mismo aquí, pero grepping las obras inversas: echo '소녀시대' | grep -P "[^\x00-\x7F]". O simplemente use the_silver_searchersegún lo indicado por @slf:echo '소녀시대' | ag "[\x80-\xFF]"
psmith
55

En perl

perl -ane '{ if(m/[[:^ascii:]]/) { print  } }' fileName > newFile
noquery
fuente
1
En OSX10.11 tuve que probar varias soluciones grep + regex antes de encontrar esto que realmente funciona
sg
¡¿Te gustaría compartir esa solución OSX @sg ?!
geotheory
La secuencia de comandos Perl anterior es la solución de la que estoy hablando
ejemplo, el
55
perl -lne 'print if /[^[:ascii:]]/' file.xml
Naveed
43

La manera fácil es definir un carácter no ASCII ... como un carácter que no es un carácter ASCII.

LC_ALL=C grep '[^ -~]' file.xml

Agregue una pestaña después de ^si es necesario.

La configuración LC_COLLATE=Cevita sorpresas desagradables sobre el significado de los rangos de caracteres en muchos entornos locales. La configuración LC_CTYPE=Ces necesaria para que coincidan los caracteres de un solo byte; de ​​lo contrario, el comando perdería secuencias de bytes no válidas en la codificación actual. La configuración LC_ALL=Cevita los efectos locales dependientes por completo.

Gilles 'SO- deja de ser malvado'
fuente
En RedHat 6.4 con tcsh, tuve que usar <<< env LC_COLLATE = C grep -n '[^ - ~]' file.xml >>>. Agregué -n para obtener el número de línea.
ddevienne
Para mí echo "A" | LC_COLLATE=C grep '[^ -~]'devuelve un partido
frabjous
1
@frabjous Si es así LC_ALL=en_US.UTF-8, eso triunfa sobre la LC_COLLATEconfiguración. ¡No deberías tener esto en tu entorno! LC_ALLes solo para forzar a una tarea específica a usar una configuración regional particular, por lo general C. Para establecer la configuración regional predeterminada para todas las categorías, establezca LANG.
Gilles 'SO- deja de ser malvado'
1
Al principio, no agregué LC_ALL=C, se comporta de manera diferente en Mac OS X y Ubuntu. Después de agregar esta configuración, dan el mismo resultado.
Max Peng
1
Esto funciona en una Mac, mientras que las otras soluciones basadas en grep no.
Matthias Fripp
26

Aquí hay otra variante que encontré que produjo resultados completamente diferentes de la búsqueda grep [\x80-\xFF]en la respuesta aceptada. Quizás sea útil para alguien encontrar caracteres adicionales que no sean ascii:

grep --color='auto' -P -n "[^[:ascii:]]" myfile.txt

Nota: el grep de mi computadora (una Mac) no tenía -Popción, así que lo hice brew install grepy comencé la llamada anterior con en ggreplugar de grep.

ryanm
fuente
2
Esta es, con mucho, la mejor respuesta, ya que funciona tanto para Mac como para Linux.
tommy.carstensen
Solo uno que funcionó para mí en Linux.
9

El siguiente código funciona:

find /tmp | perl -ne 'print if /[^[:ascii:]]/'

Reemplácelo /tmpcon el nombre del directorio que desea buscar.

usuario7417071
fuente
2
En una Mac, esto funciona, mientras que la mayoría de los basados ​​en grep no.
Matthias Fripp
9

Buscando caracteres no imprimibles. TLDR; Resumen Ejecutivo

  1. buscar caracteres de control Y unicode extendido
  2. configuración regional, por ejemplo, LC_ALL=Cnecesaria para que grep haga lo que podría esperar con unicode extendido

SO los buscadores de caracteres no ascii preferidos:

$ perl -ne 'print "$. $_" if m/[\x00-\x08\x0E-\x1F\x80-\xFF]/' notes_unicode_emoji_test

como en la respuesta superior, el grep inverso:

$ grep --color='auto' -P -n "[^\x00-\x7F]" notes_unicode_emoji_test

como en la respuesta superior pero CON LC_ALL=C:

$ LC_ALL=C grep --color='auto' -P -n "[\x80-\xFF]" notes_unicode_emoji_test

. . más . . detalles insoportables sobre esto:. . .

Estoy de acuerdo con Harvey anteriormente enterrado en los comentarios, a menudo es más útil buscar caracteres no imprimibles O es fácil pensar no ASCII cuando realmente debería estar pensando no imprimible. Harvey sugiere "use esto:" [^\n -~]". Agregue \ r para archivos de texto de DOS. Eso se traduce en" [^\x0A\x020-\x07E]"y agregue \ x0D para CR"

Además, agregar -c (mostrar el recuento de patrones coincidentes) a grep es útil cuando se buscan caracteres no imprimibles, ya que las cadenas coincidentes pueden estropear el terminal.

Descubrí que agregar el rango 0-8 y 0x0e-0x1f (al rango 0x80-0xff) es un patrón útil. Esto excluye TAB, CR y LF y uno o dos caracteres imprimibles poco comunes más. Entonces, en mi humilde opinión, un patrón grep bastante útil (aunque crudo) es éste:

grep -c -P -n "[\x00-\x08\x0E-\x1F\x80-\xFF]" *

REALMENTE, generalmente necesitarás hacer esto:

LC_ALL=C grep -c -P -n "[\x00-\x08\x0E-\x1F\x80-\xFF]" *

Descompostura:

LC_ALL=C - set locale to C, otherwise many extended chars will not match (even though they look like they are encoded > 0x80)
\x00-\x08 - non-printable control chars 0 - 7 decimal
\x0E-\x1F - more non-printable control chars 14 - 31 decimal
\x80-1xFF - non-printable chars > 128 decimal
-c - print count of matching lines instead of lines
-P - perl style regexps

Instead of -c you may prefer to use -n (and optionally -b) or -l
-n, --line-number
-b, --byte-offset
-l, --files-with-matches

Ejemplo práctico de uso find para grep todos los archivos en el directorio actual:

LC_ALL=C find . -type f -exec grep -c -P -n "[\x00-\x08\x0E-\x1F\x80-\xFF]" {} + 

Es posible que desee ajustar el grep a veces. por ejemplo, BS (0x08 - retroceso) char usado en algunos archivos imprimibles o para excluir VT (0x0B - pestaña vertical). Los caracteres BEL (0x07) y ESC (0x1B) también pueden considerarse imprimibles en algunos casos.

Non-Printable ASCII Chars
** marks PRINTABLE but CONTROL chars that is useful to exclude sometimes
Dec   Hex Ctrl Char description           Dec Hex Ctrl Char description
0     00  ^@  NULL                        16  10  ^P  DATA LINK ESCAPE (DLE)
1     01  ^A  START OF HEADING (SOH)      17  11  ^Q  DEVICE CONTROL 1 (DC1)
2     02  ^B  START OF TEXT (STX)         18  12  ^R  DEVICE CONTROL 2 (DC2)
3     03  ^C  END OF TEXT (ETX)           19  13  ^S  DEVICE CONTROL 3 (DC3)
4     04  ^D  END OF TRANSMISSION (EOT)   20  14  ^T  DEVICE CONTROL 4 (DC4)
5     05  ^E  END OF QUERY (ENQ)          21  15  ^U  NEGATIVE ACKNOWLEDGEMENT (NAK)
6     06  ^F  ACKNOWLEDGE (ACK)           22  16  ^V  SYNCHRONIZE (SYN)
7     07  ^G  BEEP (BEL)                  23  17  ^W  END OF TRANSMISSION BLOCK (ETB)
8     08  ^H  BACKSPACE (BS)**            24  18  ^X  CANCEL (CAN)
9     09  ^I  HORIZONTAL TAB (HT)**       25  19  ^Y  END OF MEDIUM (EM)
10    0A  ^J  LINE FEED (LF)**            26  1A  ^Z  SUBSTITUTE (SUB)
11    0B  ^K  VERTICAL TAB (VT)**         27  1B  ^[  ESCAPE (ESC)
12    0C  ^L  FF (FORM FEED)**            28  1C  ^\  FILE SEPARATOR (FS) RIGHT ARROW
13    0D  ^M  CR (CARRIAGE RETURN)**      29  1D  ^]  GROUP SEPARATOR (GS) LEFT ARROW
14    0E  ^N  SO (SHIFT OUT)              30  1E  ^^  RECORD SEPARATOR (RS) UP ARROW
15    0F  ^O  SI (SHIFT IN)               31  1F  ^_  UNIT SEPARATOR (US) DOWN ARROW

ACTUALIZACIÓN: tuve que volver a visitar esto recientemente. Y, YYMV dependiendo de la configuración del terminal / pronóstico del tiempo solar PERO. . Noté que grep no estaba encontrando muchos caracteres unicode o extendidos. Aunque intuitivamente deberían coincidir con el rango de 0x80 a 0xff, los caracteres unicode de 3 y 4 bytes no coincidían. ??? ¿Alguien puede explicar esto? SI. @frabjous preguntó y @calandoa explicó que LC_ALL=Cdebería usarse para establecer la configuración regional del comando para hacer coincidir grep.

Por ejemplo, mi localidad LC_ALL=vacía

$ locale
LANG=en_IE.UTF-8
LC_CTYPE="en_IE.UTF-8"
.
.
LC_ALL=

grep con LC_ALL=coincidencias vacías con caracteres de 2 bytes codificados pero no con 3 y 4 bytes codificados:

$ grep -P -n "[\x00-\x08\x0E-\x1F\x80-\xFF]" notes_unicode_emoji_test
5 copyright c2a9
7:call  underscore c2a0
9:CTRL
31:5 © copyright
32:7 call  underscore

grep with LC_ALL=Cparece coincidir con todos los caracteres extendidos que desea:

$ LC_ALL=C grep --color='auto' -P -n "[\x80-\xFF]" notes_unicode_emoji_test  
1:���� unicode dashes e28090
3:��� Heart With Arrow Emoji - Emojipedia == UTF8? f09f9298
5:� copyright c2a9
7:call underscore c2a0
11:LIVE��E! ���������� ���� ���������� ���� �� �� ���� ����  YEOW, mix of japanese and chars from other e38182 e38184 . . e0a487
29:1 ���� unicode dashes
30:3 ��� Heart With Arrow Emoji - Emojipedia == UTF8 e28090
31:5  copyright
32:7 call underscore
33:11 LIVE��E! ���������� ���� ���������� ���� �� �� ���� ����  YEOW, mix of japanese and chars from other
34:52 LIVE��E! ���������� ���� ���������� ���� �� �� ���� ����  YEOW, mix of japanese and chars from other
81:LIVE��E! ���������� ���� ���������� ���� �� �� ���� ����  YEOW, mix of japanese and chars from other

ESTA coincidencia perl (parcialmente encontrada en otro lugar en stackoverflow) O el grep inverso en la respuesta superior NO parece encontrar TODOS los caracteres "extraños" y ~ maravillosos ~ "no ascii" extraños sin establecer la configuración regional:

$ grep --color='auto' -P -n "[^\x00-\x7F]" notes_unicode_emoji_test

$ perl -ne 'print "$. $_" if m/[\x00-\x08\x0E-\x1F\x80-\xFF]/' notes_unicode_emoji_test  

1 ‐‐ unicode dashes e28090
3 💘 Heart With Arrow Emoji - Emojipedia == UTF8? f09f9298
5 © copyright c2a9
7 call  underscore c2a0
9 CTRL-H CHARS URK URK URK 
11 LIVEE! あいうえお かが アイウエオ カガ   ซฌ आइ  YEOW, mix of japanese and chars from other e38182 e38184 . . e0a487
29 1 ‐‐ unicode dashes
30 3 💘 Heart With Arrow Emoji - Emojipedia == UTF8 e28090
31 5 © copyright
32 7 call  underscore
33 11 LIVEE! あいうえお かが アイウエオ カガ   ซฌ आइ  YEOW, mix of japanese and chars from other
34 52 LIVEE! あいうえお かが アイウエオ カガ   ซฌ आइ  YEOW, mix of japanese and chars from other
73 LIVEE! あいうえお かが アイウエオ カガ   ซฌ आइ  YEOW, mix of japanese and chars from other

SO los buscadores de caracteres no ascii preferidos:

$ perl -ne 'print "$. $_" if m/[\x00-\x08\x0E-\x1F\x80-\xFF]/' notes_unicode_emoji_test

como en la respuesta superior, el grep inverso:

$ grep --color='auto' -P -n "[^\x00-\x7F]" notes_unicode_emoji_test

como en la respuesta superior pero CON LC_ALL=C:

$ LC_ALL=C grep --color='auto' -P -n "[\x80-\xFF]" notes_unicode_emoji_test
gaoithe
fuente
1
Responda por qué grep no coincide con los caracteres codificados en más de 2 bytes gracias a @calandoa y frabjous en los comentarios anteriores sobre la pregunta. Use LC_ALL = C antes del comando grep.
Gaoithe
1
¡Muchas gracias por molestarse en publicar una respuesta enterrada bajo otros 800 votos a favor! Mi problema era un personaje 0x02. Es posible que desee poner ese "ejemplo práctico de uso" cerca de la parte superior, ya que realmente no necesita leer la publicación completa para ver si ese es su problema.
Noumenon
1
Lo sé, una respuesta muy antigua, y detalles insoportables, pero es útil para mí y para otros, también espero. Tienes razón, agregué TLDR; hasta arriba.
gaoithe
1

¡Extrañamente, tuve que hacer esto hoy! Terminé usando Perl porque no podía hacer que grep / egrep funcionara (incluso en modo -P). Algo como:

cat blah | perl -en '/\xCA\xFE\xBA\xBE/ && print "found"'

Para los caracteres unicode (como \u2212en el ejemplo a continuación) use esto:

find . ... -exec perl -CA -e '$ARGV = @ARGV[0]; open IN, $ARGV; binmode(IN, ":utf8"); binmode(STDOUT, ":utf8"); while (<IN>) { next unless /\N{U+2212}/; print "$ARGV: $&: $_"; exit }' '{}' \;
dty
fuente
1

Puede ser interesante saber cómo buscar un carácter unicode. Este comando puede ayudar. Solo necesita saber el código en UTF8

grep -v $'\u200d'
arezae
fuente
Realmente no soy un experto, pero sé lo suficiente como para saber que no es una representación UTF8, es UTF16, o tal vez UTF32 o UCS16. Para un punto de código de 2 bytes, esos tres podrían ser todos iguales.
Baxissimo
1

Encontrar todos los caracteres que no son ascii da la impresión de que uno está buscando cadenas unicode o tiene la intención de quitar dichos caracteres individualmente.

Para el primero, pruebe uno de estos (la variable filese usa para la automatización):

 file=file.txt ; LC_ALL=C grep -Piao '[\x80-\xFF\x20]{7,}' $file | iconv -f $(uchardet $file) -t utf-8

 file=file.txt ; pcregrep -iao '[\x80-\xFF\x20]{7,}' $file | iconv -f $(uchardet $file) -t utf-8

 file=file.txt ; pcregrep -iao '[^\x00-\x19\x21-\x7F]{7,}' $file | iconv -f $(uchardet $file) -t utf-8

La vainilla grep no funciona correctamente sin LC_ALL = C como se señaló en las respuestas anteriores.

El rango ASCII es x00-x7F, el espacio es x20, ya que las cadenas tienen espacios, el rango negativo lo omite.

El rango no ASCII es x80-xFF, dado que las cadenas tienen espacios, el rango positivo lo agrega.

Se supone que la cadena tiene al menos 7 caracteres consecutivos dentro del rango. {7,}.

Para la salida legible por shell, uchardet $filedevuelve una suposición de la codificación del archivo que se pasa a iconv para la interpolación automática.

noabody
fuente
Esto es muy útil debido a la mención del uchardetcomando. Gracias por ese aviso!
bballdave025