¿Cómo 'grep' una corriente continua?

729

¿Es posible usarlo grepen una secuencia continua?

Lo que quiero decir es una especie de tail -f <file>comando, pero con grepla salida para mantener solo las líneas que me interesan.

Lo he intentado tail -f <file> | grep patternpero parece que grepsolo se puede ejecutar una vez que tailfinaliza, es decir, nunca.

Matthieu Napoli
fuente
99
Es muy probable que el programa que genera el archivo no esté volcando su salida.
Steve-o
tail -f filefunciona (veo la nueva salida en tiempo real)
Matthieu Napoli
66
Sería apropiado para unix.stackexchange.com
Luc M
@Luc, de hecho, no pensé en eso
Matthieu Napoli
¿Puede haber no hay nuevas líneas en su flujo de entrada? Si es así, grep no procederá.
Lynch el

Respuestas:

1328

Active el grepmodo de almacenamiento en línea de la memoria intermedia cuando utilice BSD grep (FreeBSD, Mac OS X, etc.)

tail -f file | grep --line-buffered my_pattern

No necesita hacer esto para GNU grep (usado en prácticamente cualquier Linux) ya que se vaciará por defecto (YMMV para otros gustos de Unix como SmartOS, AIX o QNX).

tad
fuente
3
@MichaelNiemand puedes usar el archivo tail -F | grep --line-buffered my_pattern
jcfrei
47
@MichaelGoldshteyn Tómelo con calma. Las personas lo votan porque encuentran esta página cuando buscan en Google "grep line buffer" y les resuelve un problema que puede no ser exactamente el que se plantea como la pregunta.
llover
44
Vine aquí tratando de aprovechar la salida de strace. Sin el --line-buffered, no funcionará.
sjas
55
@MichaelGoldshteyn (y los partidarios de su comentario): Siempre he tenido este problema tail -f | grepy lo --line-bufferedresuelve por mí (en Ubuntu 14.04, GNU grep versión 2.16). ¿Dónde está implementada la lógica "usar el almacenamiento en línea de la memoria intermedia si stdout es un tty"? En git.savannah.gnu.org/cgit/grep.git/tree/src/grep.c , line_bufferedsolo lo establece el analizador de argumentos.
Aasmund Eldhuset
8
@MichaelGoldshteyn Estoy en macOS usando BSD grep y sin --line-bufferedobtener ningún resultado. Sin embargo, después de las pruebas, parece que GNU grep hace lo que usted describe. Entonces, como la mayoría de las cosas Unix, depende de la implementación de su plataforma. Dado que la pregunta no especificaba la plataforma, su información parece ser falsa: después de revisar el código para BSD grep y compararlo con GNU grep, el comportamiento está definitivamente controlado por la opción --line-buffer. Es solo que solo GNU grep se descarga por defecto.
Richard Waite
119

Lo uso tail -f <file> | grep <pattern>todo el tiempo.

Esperará hasta que grep se ruborice, no hasta que termine (estoy usando Ubuntu).

Irit Katriel
fuente
44
Que puede durar bastante tiempo, así que trata de no impacientarte.
glglgl
¿Cuánto tiempo puede tomar aproximadamente?
Matthieu Napoli
@Matthieu: depende principalmente de lo que desees y del tamaño de los búferes en tu sistema operativo. Si el grep solo coincide con una cadena corta cada pocas horas, pasarán días antes de la primera descarga.
tripleee el
13
Tail no usa buffering de salida - grep sí.
XzKto
77
No, grep no realiza el almacenamiento en búfer de salida cuando la salida va a un dispositivo tty, como claramente está en esta respuesta. ¡Hace buffer de línea! Esta es la respuesta correcta y debe ser la respuesta aceptada. Consulte mi comentario más extenso sobre la respuesta actualmente aceptada ( incorrecta ) para obtener más detalles.
Michael Goldshteyn
67

Creo que su problema es que grep usa algo de almacenamiento en búfer de salida. Tratar

tail -f file | stdbuf -o0 grep my_pattern

establecerá el modo de almacenamiento en búfer de salida de grep en sin búfer.

XzKto
fuente
77
Y esto tiene la ventaja de que se puede usar para muchos otros comandos además grep.
Peter V. Mørch
44
Sin embargo, como descubrí después de jugar más con él, algunos comandos solo vacían su salida cuando se conectan a un tty, y para eso, unbuffer(en el expect-devpaquete de debian) es el rey . Entonces usaría unbuffer sobre stdbuf.
Peter V. Mørch
55
@Peter V. Mørch Sí, tienes razón, unbuffer a veces puede funcionar donde stdbuf no. Pero creo que está tratando de encontrar un programa 'mágico' que siempre solucione sus problemas en lugar de comprenderlos. Crear un tty virtual no es una tarea relacionada. Stdbuf hace exactamente lo que queremos (establece el búfer de salida estándar para dar valor), mientras que unbuffer hace muchas cosas ocultas que quizás no queramos (compare interactivo topcon stdbuf y unbuffer). Y realmente no existe una solución 'mágica': el unbuffer falla a veces también, por ejemplo awk usa una implementación de buffer diferente (stdbuf también fallará).
XzKto
2
"Pero creo que está tratando de encontrar un programa 'mágico' que siempre solucione sus problemas en lugar de comprender su problema". - ¡Creo que tienes razón! ;-)
Peter V. Mørch
1
Más información sobre stdbuf, `unbuffer, y stdio buffering en pixelbeat.org/programming/stdio_buffering
Tor Klingberg
13

Si desea encontrar coincidencias en todo el archivo (no solo la cola), y desea que se asiente y espere nuevas coincidencias, esto funciona bien:

tail -c +0 -f <file> | grep --line-buffered <pattern>

La -c +0bandera dice que la salida debe iniciar 0bytes ( -c) desde el principio ( +) del archivo.

Ken Williams
fuente
12

En la mayoría de los casos, puede tail -f /var/log/some.log |grep foohacerlo y funcionará bien.

Si necesita usar múltiples greps en un archivo de registro en ejecución y encuentra que no obtiene salida, es posible que deba pegar el --line-bufferedinterruptor en su grep medio (s), de esta manera:

tail -f /var/log/some.log | grep --line-buffered foo | grep bar
Dale Anderson
fuente
7

puede considerar esta respuesta como una mejora ... generalmente estoy usando

tail -F <fileName> | grep --line-buffered  <pattern> -A 3 -B 5

-F es mejor en caso de rotación del archivo (-f no funcionará correctamente si el archivo gira)

-A y -B es útil para obtener líneas justo antes y después de la aparición del patrón ... estos bloques aparecerán entre los separadores de líneas discontinuas

Pero para mí prefiero hacer lo siguiente

tail -F <file> | less

Esto es muy útil si desea buscar dentro de los registros transmitidos. Me refiero a retroceder y avanzar y mirar profundamente

mebada
fuente
44
grep -C 3 <pattern>, reemplaza -A <N> y -B <N> si N es igual.
AKS
6

No vi a nadie ofrecer mi habitual para esto:

less +F <file>
ctrl + c
/<search term>
<enter>
shift + f

Prefiero esto, porque puedes usarlo ctrl + cpara detenerte y navegar por el archivo cuando lo desees , y luego simplemente presionar shift + fpara volver a la búsqueda de transmisión en vivo.

Hans.Loven.work
fuente
4

sed sería una mejor opción ( editor de flujo )

tail -n0 -f <file> | sed -n '/search string/p'

y luego si desea que el comando de cola salga una vez que encuentre una cadena particular:

tail --pid=$(($BASHPID+1)) -n0 -f <file> | sed -n '/search string/{p; q}'

Obviamente un bashism: $ BASHPID será la identificación del proceso del comando tail. El comando sed es el siguiente después de la cola en la tubería, por lo que la identificación del proceso sed será $ BASHPID + 1.

Christian Herr
fuente
1
La suposición de que el próximo proceso iniciado en el sistema ( $BASHPID+1) será suyo es falso en muchas situaciones, y esto no hace nada para resolver el problema de almacenamiento en búfer, que probablemente es lo que el OP estaba tratando de preguntar. En particular, la recomendación sedmás grepaquí parece simplemente una cuestión de preferencia (dudosa). (Usted puede obtener p;qun comportamiento con grep -m 1si ese es el punto que está tratando de entregar.)
tripleee
Funciona, el comando sed imprime cada línea tan pronto como esté lista, el comando grep --line-bufferedno lo hizo. Sinceramente, no entiendo el menos 1.
MUY Bélgica
Hasta ahora se ha establecido que el almacenamiento en búfer es el problema con grep . No se requiere ninguna acción especial para manejar el almacenamiento en línea usando sed , es un comportamiento predeterminado, de ahí mi énfasis en el flujo de palabras . Y es cierto, no hay garantía de que $ BASHPID + 1 sea el pid correcto a seguir, pero dado que la asignación de pid es secuencial y al comando canalizado se le asigna un pid inmediatamente después, es completamente probable.
Christian Herr el
1

Sí, esto realmente funcionará bien. Grepy la mayoría de los comandos de Unix operan en transmisiones una línea a la vez. Cada línea que sale de la cola se analizará y pasará si coincide.

Caleb
fuente
2
Eso no es realmente correcto. Si grepes el último comando en la cadena de tuberías, actuará como usted explica. Sin embargo, si está en el medio, almacenará alrededor de 8k de salida a la vez.
Mahmoud Al-Qudsi
1

Este comando funciona para mí (Suse):

mail-srv:/var/log # tail -f /var/log/mail.info |grep --line-buffered LOGIN  >> logins_to_mail

recolectando inicios de sesión al servicio de correo

usuario10584393
fuente
-1

ciertamente no tendrás éxito con

tail -f /var/log/foo.log |grep --line-buffered string2search

cuando usa "colortail" como un alias para la cola, por ejemplo. en bash

alias tail='colortail -n 30'

puede verificar por tipo alias si esto genera algo como tail isan alias of colortail -n 30. entonces tienes tu culpable :)

Solución:

eliminar el alias con

unalias tail

asegúrese de que está utilizando el binario de cola 'real' con este comando

type tail

que debería generar algo como:

tail is /usr/bin/tail

y luego puedes ejecutar tu comando

tail -f foo.log |grep --line-buffered something

Buena suerte.

usuario882786
fuente
-4

¡Usa awk (otra gran utilidad bash) en lugar de grep donde no tienes la opción de buffer de línea! Continuamente transmitirá sus datos desde la cola.

así es como usas grep

tail -f <file> | grep pattern

Así es como usarías awk

tail -f <file> | awk '/pattern/{print $0}'
Atif
fuente
66
Esto no es correcto; Awk, listo para usar, realiza el almacenamiento en línea, al igual que la mayoría de las otras herramientas estándar de Unix. (Además, {print $0}es redundante, ya que la impresión es la acción predeterminada cuando pasa una condición).
triplicado