¿Qué hace que grep considere un archivo como binario?

185

Tengo algunos volcados de base de datos de un sistema Windows en mi caja. Son archivos de texto. Estoy usando cygwin para atravesarlos. Estos parecen ser archivos de texto sin formato; Los abro con editores de texto como el bloc de notas y el wordpad y se ven legibles. Sin embargo, cuando corro grep sobre ellos, dirá binary file foo.txt matches.

He notado que los archivos contienen algunos NULcaracteres ascii , que creo que son artefactos del volcado de la base de datos.

Entonces, ¿qué hace que grep considere que estos archivos son binarios? El NULpersonaje? ¿Hay una bandera en el sistema de archivos? ¿Qué necesito cambiar para que grep me muestre las coincidencias de línea?

usuario394
fuente
2
--null-datapuede ser útil si NULes el delimitador.
Steve-o

Respuestas:

126

Si hay un NULcarácter en cualquier parte del archivo, grep lo considerará como un archivo binario.

Puede haber una solución como esta cat file | tr -d '\000' | yourgreppara eliminar todos los valores nulos primero y luego buscar a través del archivo.

bbaja42
fuente
149
... o use -a/ --text, al menos con GNU grep.
derobert el
1
@derobert: en realidad, en algunos sistemas (más antiguos), grep ve líneas, pero su salida truncará cada línea coincidente al principio NUL(¿probablemente porque llama a printf de C y le da la línea coincidente?). En dicho sistema grep cmd .sh_history, a devolverá tantas líneas vacías como líneas que coincidan con 'cmd', ya que cada línea de sh_history tiene un formato específico con un NULal comienzo de cada línea. (pero su comentario "al menos sobre GNU grep" probablemente se haga realidad. No tengo uno a mano en este momento para probar, pero espero que manejen esto muy bien)
Olivier Dulac
44
¿Es la presencia de un personaje NUL el único criterio? Lo dudo. Probablemente sea más inteligente que eso. Supongo que cualquier cosa que esté fuera del rango Ascii 32-126, pero tendríamos que mirar el código fuente para estar seguros.
Michael Martinez
2
Mi información era de la página del manual de la instancia grep específica. Su comentario sobre la implementación es válido, la fuente triunfa sobre los documentos.
bbaja42
2
Tenía un archivo que grepen cygwin consideraba binario porque tenía un guión largo (0x96) en lugar de un guión ASCII regular / menos (0x2d). Supongo que esta respuesta resolvió el problema del OP, pero parece que está incompleto.
cp.engr
121

grep -a trabajó para mi:

$ grep --help
[...]
 -a, --text                equivalent to --binary-files=text
Plouff
fuente
44
Esta es la mejor y menos costosa respuesta IMO.
pydsigner
Pero no cumple con POSIX
Matteo
21

Puede utilizar la stringsutilidad para extraer el contenido del texto de cualquier archivo y luego canalizarla a través de grep, por ejemplo: strings file | grep pattern.

holgero
fuente
2
Ideal para grepping archivos de registro que podrían estar parcialmente dañados
Hannes R.
Sí, a veces también ocurre el registro mixto binario. Esto es bueno.
sdkks
13

GNU grep 2.24 RTFS

Conclusión: solo 2 y 2 casos:

  • NUL, p.ej printf 'a\0' | grep 'a'

  • error de codificación según el C99 mbrlen(), por ejemplo:

    export LC_CTYPE='en_US.UTF-8'
    printf 'a\x80' | grep 'a'
    

    porque \x80no puede ser el primer byte de un punto Unicode UTF-8 : UTF-8 - Descripción | en.wikipedia.org

Además, como lo menciona Stéphane Chazelas ¿Qué hace que grep considere que un archivo es binario? El | Unix y Linux Stack Exchange , esas comprobaciones solo se realizan hasta la primera lectura de búfer de longitud TODO.

Solo hasta la primera lectura del búfer

Por lo tanto, si se produce un error de codificación o NUL en el medio de un archivo muy grande, de todos modos podría aparecer grep.

Me imagino que esto es por razones de rendimiento.

Por ejemplo: esto imprime la línea:

printf '%10000000s\n\x80a' | grep 'a'

pero esto no:

printf '%10s\n\x80a' | grep 'a'

El tamaño real del búfer depende de cómo se lea el archivo. Por ejemplo, comparar:

export LC_CTYPE='en_US.UTF-8'
(printf '\n\x80a') | grep 'a'
(printf '\n'; sleep 1; printf '\x80a') | grep 'a'

Con el sleep, la primera línea se pasa a grep incluso si solo tiene 1 byte porque el proceso se detiene, y la segunda lectura no comprueba si el archivo es binario.

RTFS

git clone git://git.savannah.gnu.org/grep.git 
cd grep
git checkout v2.24

Encuentre dónde está codificado el mensaje de error stderr:

git grep 'Binary file'

Nos lleva a /src/grep.c:

if (!out_quiet && (encoding_error_output
                    || (0 <= nlines_first_null && nlines_first_null < nlines)))
    {
    printf (_("Binary file %s matches\n"), filename);

Si esas variables estaban bien nombradas, básicamente llegamos a la conclusión.

encoding_error_output

Grepping rápido para encoding_error_outputmuestra que la única ruta de código que puede modificarlo pasa por buf_has_encoding_errors:

clen = mbrlen (p, buf + size - p, &mbs);
if ((size_t) -2 <= clen)
  return true;

entonces solo man mbrlen.

nlines_first_null y nlines

Inicializado como:

intmax_t nlines_first_null = -1;
nlines = 0;

así que cuando se encuentra un nulo se 0 <= nlines_first_nullconvierte en verdad.

TODO cuando puede nlines_first_null < nlinesser falso? Me volví flojo.

POSIX

No define las opciones binarias grep: busca en un archivo un patrón | pubs.opengroup.org , y GNU grep no lo documenta, por lo que RTFS es la única forma.

Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
fuente
1
¡Impresionante explicación!
user394
2
Tenga en cuenta que la comprobación de UTF-8 válida solo ocurre en configuraciones regionales UTF-8. También tenga en cuenta que la verificación solo se realiza en la primera lectura del búfer del archivo, que para un archivo normal parece tener 32768 bytes en mi sistema, pero para una tubería o socket puede ser tan pequeño como un byte. Comparar (printf '\n\0y') | grep ycon (printf '\n'; sleep 1; printf '\0y') | grep ypor ejemplo.
Stéphane Chazelas
@ StéphaneChazelas "Tenga en cuenta que la comprobación de UTF-8 válida solo ocurre en entornos locales UTF-8": ¿quiere decir acerca de lo export LC_CTYPE='en_US.UTF-8'que en mi ejemplo, o algo más? Buf read: ejemplo increíble, agregado a la respuesta. Obviamente has leído la fuente más que yo, me recuerda a esos hackers koans "El estudiante estaba iluminado" :-)
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
1
Tampoco
examiné
1
@CiroSantilli 巴拿馬 文件 六四 事件 法轮功 ¿contra qué versión de GNU grep probaste?
jrw32982
6

Grep de repente vio uno de mis archivos de texto como binario:

$ file foo.txt
foo.txt: ISO-8859 text

La solución fue convertirlo usando iconv:

iconv -t UTF-8 -f ISO-8859-1 foo.txt > foo_new.txt
zzapper
fuente
1
Esto me pasó a mí también. En particular, la causa fue un espacio sin interrupción codificado con ISO-8859-1, que tuve que reemplazar con un espacio regular para que grep buscara en el archivo.
Gallaecio
44
grep 2.21 trata los archivos de texto ISO-8859 como si fueran binarios, agregue export LC_ALL = C antes del comando grep.
netawater
@netawater ¡Gracias! Este es, por ejemplo, el caso si tiene algo como Müller en un archivo de texto. Eso es 0xFChexadecimal, por lo que fuera del rango que grep esperaría para utf8 (hasta 0x7F). Verifique con printf 'a \ x7F' | grep 'a' como Ciro describe arriba.
Anne van Rossum
5

El archivo /etc/magico /usr/share/misc/magictiene una lista de secuencias que el comando fileusa para determinar el tipo de archivo.

Tenga en cuenta que el binario puede ser una solución alternativa. A veces, los archivos con codificación extraña también se consideran binarios.

grepen Linux tiene algunas opciones para manejar archivos binarios como --binary-fileso-U / --binary

klapaucius
fuente
Más precisamente, error de codificación según C99's mbrlen(). Ejemplo e interpretación de la fuente en: unix.stackexchange.com/a/276028/32558
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
2

Uno de mis alumnos tuvo este problema. Hay un error grepen Cygwin. Si el archivo tiene caracteres que no son Ascii, grepy véalo egrepcomo binario.

Joan Pontius
fuente
Eso suena como una característica, no un error. Especialmente dado que hay una opción de línea de comandos para controlarlo (-a / --text)
Will Sheppard
2

En realidad, respondiendo a la pregunta "¿Qué hace que grep considere que un archivo es binario?", Puede usar iconv:

$ iconv < myfile.java
iconv: (stdin):267:70: cannot convert

En mi caso, había caracteres españoles que aparecían correctamente en los editores de texto, pero grep los consideraba binarios; iconvla salida me señaló los números de línea y columna de esos caracteres

En el caso de los NULcaracteres, iconvlos considerará normales y no imprimirá ese tipo de salida, por lo que este método no es adecuado.

golimar
fuente
1

Yo tuve el mismo problema. Solía vi -b [filename]ver los caracteres añadidos. Encontré los personajes de control ^@y ^M. Luego, en vi escriba :1,$s/^@//gpara eliminar los ^@caracteres. Repita este comando para ^M.

Advertencia: para obtener los caracteres de control "azules", presione Ctrl+ y vluego Ctrl+ Mo Ctrl+ @. Luego guardar y salir vi.

No es seguro
fuente