grep recursivo no enumera coincidencias de algunos archivos

Aug 20 2020

Estoy usando GNU grep 3.4 para encontrar scripts que contengan un patrón determinado. Por esto, llamo greprecursivamente así

grep -rin . -e "pattern" 

El patrón es solo una palabra, no una expresión regular. Lo extraño es que la salida no enumera las ocurrencias en algunos archivos que definitivamente contienen la cadena.

Intenté abrir estos archivos vimy buscar allí usando /patterny encuentra el patrón. La codificación se muestra en vimformato [dos:utf-8:]. Cuando copio la línea y la escribo en un nuevo archivo, el grepcomando anterior lo enumera correctamente.

¿Por qué no grepincluye el archivo original?

Respuestas

1 RedGrittyBrick Aug 20 2020 at 20:08

Grep (o al menos los mayores) no entienden UTF8. Por tanto, los caracteres compuestos, los puntos de separación de sílabas u otros datos invisibles pueden desalentar a grep.

Grep también se ve afectado por los valores de $LC_ALL, $LC_CTYPE y $ LANG.

Use vim para guardar algunas líneas alrededor de una palabra que grep no pudo encontrar y luego haga un hexadecimal de ese pequeño archivo de ejemplo. Probablemente verás por qué falló grep

También puede usar comandos vim ( ga, g8etc.) para examinar caracteres, pero un volcado hexadecimal puede ser más claro

1 buddemat Aug 20 2020 at 22:28

Encontré el problema (con la ayuda de otra respuesta). Los archivos 'grep' no mostraron ningún resultado porque no estaban realmente utf-8codificados, pero utf-16be. Aprendí esto usando hexdump (créditos a @RedGrittyBrick):

hd file_for_which_grep_works_as_expected.txt

cedido

00000000  20 20 20 20 50 61 74 74  65 72 6e 0a              |    Pattern.|
0000000c

mientras

hd file_for_which_grep_fails.txt

regresó

00000000  fe ff 00 50 00 61 00 74  00 74 00 65 00 72 00 6e  |...P.a.t.t.e.r.n|
00000010  00 0a                                             |..|
00000012

Entonces, verificando la codificación con

file -i file_for_which_grep_fails.txt

lo identificó como text/plain; charset=utf-16be.

No reconocí que los utf-8mostrados por vimeran en realidad la codificación del búfer , no la codificación del archivo . Ejecutar :set fileencodingen vimTambién se muestra correctamente fileencoding=utf-16(que se encuentra aquíhttps://superuser.com/a/28783/1210682).

Entonces, el problema es que my grepno funciona con utf-16archivos codificados. Esto ya se ha descrito aquí:https://superuser.com/a/231471/1210682. Sin embargo, el remedio de convertir utf-16archivos a utf-8antes grepno funciona cuando lo uso de forma recursiva, ya que no sé de antemano qué archivos pueden ser utf-8y cuáles, utf-16y estoy buscando en muchos archivos.

Existen diferentes soluciones, dos de las cuales voy a describir brevemente aquí:

  1. Una solución rápida y sucia que funcionó para mí fue expandir el patrón de búsqueda para incluir uno que coincidiera con la utf-16versión y buscar uno de ambos patrones:

    grep -riPa . -e "pattern|p.a.t.t.e.r.n."
    

    Por supuesto, esto es muy limitado en términos de posibles patrones.

  2. Existen alternativas a Me grepgusta ugrepo ripgrepque (entre otras cosas) pueden manejar utf-16archivos. Terminé usando lo ripgrepque está disponible en los repositorios de paquetes estándar de Ubuntu desde 18.04 en adelante:

    rg -i "pattern"  
    

Hay una gran discusión sobre alternativas aquí: https://stackoverflow.com/questions/3752913/grepping-binary-files-and-utf16, entre ellos un enfoque interesante que intenta convertir el patrón de búsqueda utf-16y alimentarlo a grep. Sin embargo, no pude hacer que funcionara.