¿Por qué 'grep -q' consume todo el archivo de entrada?

23

Considere el siguiente archivo de entrada:

1
2
3
4

Corriendo

{ grep -q 2; cat; } < infile

no imprime nada Esperaría que se imprima

3
4

Puedo obtener el resultado esperado si lo cambio a

{ sed -n 2q; cat; } < infile

¿Por qué el primer comando no imprime el resultado esperado?
Es un archivo de entrada que se puede buscar y según el estándar en OPCIONES :

-q
      Quiet. Nothing shall be written to the standard output, regardless of 
      matching lines. Exit with zero status if an input line is selected.

y más abajo, bajo USO DE LA APLICACIÓN (enfatizar el mío):

La -qopción proporciona un medio para determinar fácilmente si existe o no un patrón (o cadena) en un grupo de archivos. Al buscar varios archivos, proporciona una mejora en el rendimiento ( porque puede salir tan pronto como encuentra la primera coincidencia ) [...]

Ahora, según el mismo estándar (en Introducción , en ARCHIVOS DE ENTRADA )

Cuando una utilidad estándar lee un archivo de entrada que se puede buscar y finaliza sin un error antes de que llegue al final del archivo, la utilidad se asegurará de que el desplazamiento del archivo en la descripción del archivo abierto se coloque correctamente justo después del último byte procesado por la utilidad [. ..]

tail -n +2 file
(sed -n 1q; cat) < file
...

El segundo comando es equivalente al primero solo cuando el archivo es buscable.


¿Por qué grep -qconsume todo el archivo?


Esto es gnu grepsi importa (aunque Kusalananda acaba de confirmar que sucede lo mismo en OpenBSD)

don_crissti
fuente
OpenBSD grepes una bifurcación de algo llamado FreeGrep , si alguien se lo pregunta.
Kusalananda

Respuestas:

37

grep se detiene temprano, pero amortigua su entrada por lo que su prueba es demasiado corta (y sí, me doy cuenta de que mi prueba es imperfecta ya que no se puede buscar):

seq 1 10000 | (grep -q 2; cat)

comienza en 6776 en mi sistema. Eso coincide con el búfer de 32 KB utilizado por defecto en GNU grep:

seq 1 6775 | wc

salidas

   6775    6775   32768

Tenga en cuenta que POSIX solo menciona mejoras de rendimiento

Al buscar varios archivos

Eso no establece expectativas para las mejoras de rendimiento debido a la lectura parcial de un solo archivo.

Stephen Kitt
fuente
2

Obviamente, esto se debe al almacenamiento en búfer que grepacelera las cosas. Hay herramientas que están específicamente diseñadas para leer tantos caracteres como se solicite y no más. Uno de ellos es expect:

{ expect -c "log_user 0; expect 2"; cat; } < infile

No tengo un sistema para probar esto, pero creo que expectse comerá todo hasta que encuentre la cadena esperada ( 2), y luego termine, dejando el resto de la entrada para cat.

Dmitry Grigoryev
fuente
1

Estás confundiendo sed y grep.

Para el comando sed, -2qse dice que salga de la iteración actual si en la segunda línea, la -nopción dice que funcione de manera silenciosa, por lo que obtendrá todas las líneas después de la segunda.

El comando grep se ejecuta de manera predeterminada para generar todas las líneas coincidentes, pero la -qopción dice no generar nada en stdout. entonces, si la entrada contiene un "2", tendrá un valor de salida de ÉXITO, de lo contrario FALLA. Cuáles son esos depende de su sistema operativo y shell. Por lo tanto, normalmente diría si una línea coincide al examinar el valor de salida del proceso grep. Esto es útil en una tubería donde desea saber si su entrada contiene algún valor como prueba. P.ej

if grep -q 'crash' <somelog.log ; then report_crash_via_email ; fi

En este caso, realmente no nos importa ver todas las líneas coincidentes, solo nos importa si existe al menos una. El report_crash_via_emailproceso / función puede luego apagarse y volver a abrir el archivo, o no.

Si desea que su proceso grep se DETENGA después de encontrar el carácter "2", no lo hará de manera predeterminada, inspeccionará cada línea para ver si coincide, debe decirle que lo haga. El cambio de línea de comando para eso es -m <value>. Entonces para su caso grep -q -m1 2,.

usuario212377
fuente
66
Su respuesta es información útil para uso general, greppero esta pregunta es sobre algo más sutil y esotérico. Parece que has leído la pregunta demasiado rápido para entender el comportamiento real que se está consultando. Además, GNU grep hace la búsqueda de parada cuando se utiliza con -q(según lo permitido en la cita de la especificación POSIX): La página del manual para los estados de GNU grep que “Salir [S] inmediatamente con el estado cero si un partido se encontró” . FWIW, he editado tu pregunta para mostrar cómo puedes formatear publicaciones futuras. Bienvenido a Stack Exchange .
Anthony G - justicia para Monica
Dicho esto, la respuesta de @ user212377 es correcta: en este caso grepse le pregunta si existe '2' en el archivo, nada más y nada menos. No se comporta como sedy consume registros hasta ese punto y deja el resto para su posterior procesamiento. Se lee hasta que sabe que hay un '2' o que no existe, cierra el archivo y devuelve el resultado.
Keith Davies el
grepde hecho, solo "consume todo el archivo" (ignorando las consideraciones de almacenamiento en búfer) si la cadena de búsqueda no está presente en el archivo (lo cual se puede probar solo examinando todo el archivo). Cualquier cosa menos que eso, la lectura del archivo se detiene , el archivo se cierra y el ÉXITO regresó.
Keith Davies el