Haz una cola -F hasta que coincida con un patrón

Quiero hacer una cola -F en un archivo hasta que coincida con un patrón. Encontré una forma de usar awk, pero en mi humilde opinión mi comando no está realmente limpio. El problema es que necesito hacerlo en una sola línea, debido a algunas limitaciones.

tail -n +0 -F /tmp/foo | \ awk -W interactive '{if ($1 == "EOF") exit; print} END {system("echo EOF >> /tmp/foo")}' 

La cola se bloqueará hasta que aparezca EOF en el archivo. Funciona bastante bien El bloque END es obligatorio porque la “salida” de awk no termina de inmediato. Hace awk evaluar el bloque END antes de abandonar. El bloque END se cuelga en una llamada de lectura (debido a la cola), por lo que lo último que tengo que hacer es escribir otra línea en el archivo para obligar a la cola a salir.

¿Alguien sabe una mejor manera de hacer eso?

    Prueba esto:

     sh -c 'tail -n +0 -f /tmp/foo | { sed "/EOF/ q" && kill $$ ;}' 

    Toda la línea de comando saldrá tan pronto como se vea la cadena “EOF” en / tmp / foo.

    Hay un efecto secundario: el proceso de cola se dejará en ejecución (en el fondo) hasta que se escriba algo en / tmp / foo.

    Use la opción –pid de la cola y la cola se detendrá cuando el proyectil muera. No es necesario agregar extra al archivo de cola.

     sh -c 'tail -n +0 --pid=$$ -f /tmp/foo | { sed "/EOF/ q" && kill $$ ;}' 

    No tengo resultados con la solución:

     sh -c 'tail -n +0 -f /tmp/foo | { sed "/EOF/ q" && kill $$ ;}' 

    Hay algún problema relacionado con el búfer porque si no hay más líneas anexadas al archivo, entonces sed no leerá la entrada. Entonces, con un poco más de investigación, se me ocurrió esto:

     sed '/EOF/q' < (tail -n 0 -f /tmp/foo) 

    El script está en https://gist.github.com/2377029

    ¿Esto funciona para tí?

     tail -n +0 -F /tmp/foo | sed '/EOF/q' 

    Asumo que ‘EOF’ es el patrón que estás buscando. El comando sed cierra cuando lo encuentra, lo que significa que la tail debe salir la próxima vez que se escribe.

    Supongo que existe la posibilidad de que la tail se mantenga si el patrón se encuentra al final del archivo, esperando que aparezca más en el archivo que nunca aparecerá. Si eso es realmente una preocupación, probablemente podrías matarlo: la tubería como un todo terminará cuando sed termine (a menos que estés usando un gracioso caparazón que decida que ese no es el comportamiento correcto).


    Grump sobre Bash

    Como se temía, bash (en MacOS X, al menos, pero probablemente en todas partes) es un caparazón que cree que debe esperar esperando que la tail termine aunque sed quit. Algunas veces, con más frecuencia de la que prefiero, prefiero el comportamiento del buen viejo shell de Bourne, que no era tan ingenioso y, por lo tanto, adivinaba mal con menos frecuencia que Bash. dribbler es un progtwig que transmite mensajes uno por segundo (‘1: Hello’, etc. en el ejemplo), y la salida va a la salida estándar. En Bash, esta secuencia de comandos se cuelga hasta que hice ‘ echo pqr >>/tmp/foo ‘ en una ventana separada.

     date { timeout -t 2m dribbler -t -m Hello; echo EOF; } >/tmp/foo & echo Hi sleep 1 # Ensure /tmp/foo is created tail -n +0 -F /tmp/foo | sed '/EOF/q' date 

    Tristemente, no veo de inmediato una opción para controlar este comportamiento. Encontré shopt lithist , pero eso no está relacionado con este problema.

    Hooray para Korn Shell

    Observo que cuando ejecuto ese script usando el shell Korn, funciona como esperaba, dejando una tail acecho para que me maten de alguna manera. Lo que funciona allí es ‘ echo pqr >> /tmp/foo ‘ después de que finalice el segundo comando de fecha.

    Esto es algo en lo que Tcl es bastante bueno. Si lo siguiente es “tail_until.tcl”,

     #!/usr/bin/env tclsh proc main {filename pattern} { set pipe [open "| tail -n +0 -F $filename"] set pid [pid $pipe] fileevent $pipe readable [list handler $pipe $pattern] vwait ::until_found catch {exec kill $pid} } proc handler {pipe pattern} { if {[gets $pipe line] == -1} { if {[eof $pipe]} { set ::until_found 1 } } else { puts $line if {[string first $pattern $line] != -1} { set ::until_found 1 } } } main {*}$argv 

    Entonces harías tail_until.tcl /tmp/foo EOF

    Aquí hay una versión extendida de la solución de Jon que usa sed en lugar de grep para que la salida de tail vaya a stdout:

     sed -r '/EOF/q' < ( exec tail -n +0 -f /tmp/foo ); kill $! 2> /dev/null 

    Esto funciona porque sed se crea antes de la cola, ¡así que $! sostiene el PID de la cola

    La principal ventaja de esto sobre las soluciones sh -c es que matar a sh parece imprimir algo en la salida, como ‘Terminated’, lo que no es bienvenido.

     sh -c 'tail -n +0 --pid=$$ -f /tmp/foo | { sed "/EOF/ q" && kill $$ ;}' 

    Aquí el problema principal es con $$ . Si ejecuta el comando como está, $$ está configurado no para sh sino para el PID del shell actual donde se ejecuta el comando.

    Para que kill funcione, debes cambiar kill $$ por kill \$$

    Después de eso, puede deshacerse con seguridad de --pid=$$ pasado al comando tail.

    Resumiendo, lo siguiente funcionará bien:

     /bin/sh -c 'tail -n 0 -f /tmp/foo | { sed "/EOF/ q" && kill \$$ ;} 

    Opcionalmente puede pasar -n a sed para mantenerlo en silencio 🙂

    Para matar también el proceso de tail colgante, puedes ejecutar el comando de tail en un contexto de sustitución de proceso (Bash) que luego se puede matar como si se tratara de un proceso de fondo. (Código tomado de Cómo leer una línea de ‘tail -f’ a través de una tubería, y luego terminar? ).

     : > /tmp/foo grep -m 1 EOF < ( exec tail -f /tmp/foo ); kill $! 2> /dev/null echo EOF > /tmp/foo # terminal window 2 

    Como alternativa, puede usar un tubo con nombre.

     ( : > /tmp/foo rm -f pidfifo mkfifo pidfifo sh -c '(tail -n +0 -f /tmp/foo & echo $! > pidfifo) | { sed "/EOF/ q" && kill $(cat pidfifo) && kill $$ ;}' ) echo EOF > /tmp/foo # terminal window 2 

    listo para usar para tomcat =

    sh -c ‘cola -f –pid = $$ catalina.out | {grep -i -m 1 “Inicio del servidor en” && kill $$;} ‘

    para el escenario anterior:

     sh -c 'tail -f --pid=$$ /tmp/foo | { grep -i -m 1 EOF && kill $$ ;}' 
     tail -f  | grep -q ""