Estoy usando tail -f para monitorear un archivo de registro en el que se está escribiendo activamente. Cuando una determinada cadena se escribe en el archivo de registro, quiero salir de la supervisión y continuar con el resto de mi secuencia de comandos.
Actualmente estoy usando:
tail -f logfile.log | grep -m 1 "Server Started"
Cuando se encuentra la cadena, grep se cierra como se esperaba, pero también necesito encontrar una manera de hacer que el comando de cola se cierre para que el script pueda continuar.
tail
dados solo en la siguiente línea. Intente esto:date > log; tail -f log | grep -m 1 trigger
y luego en otro shell:echo trigger >> log
y verá la salidatrigger
en el primer shell, pero no termina el comando. Luego intente:date >> log
en el segundo shell y el comando en el primer shell terminará. Pero a veces esto es demasiado tarde; queremos terminar tan pronto como aparezca la línea de activación, no cuando la línea después de que se complete la línea de activación.tail
+grep -q
como la respuesta de 00prometheusRespuestas:
Un simple POSIX de una sola línea
Aquí hay una línea simple. No necesita trucos específicos de bash o no POSIX, ni siquiera una tubería con nombre. Todo lo que realmente necesita es desacoplar la terminación de
tail
fromgrep
. De esa manera, una vez quegrep
finaliza, el script puede continuar incluso sitail
aún no ha finalizado. Entonces, este método simple lo llevará allí:grep
se bloqueará hasta que haya encontrado la cadena, con lo que saldrá. Altail
ejecutar desde su propio sub-shell, podemos colocarlo en segundo plano para que se ejecute de forma independiente. Mientras tanto, el shell principal es libre de continuar la ejecución del script tan pronto comogrep
salga.tail
permanecerá en su sub-shell hasta que la siguiente línea se haya escrito en el archivo de registro, y luego saldrá (posiblemente incluso después de que el script principal haya finalizado). El punto principal es que la tubería ya no espera atail
que termine, por lo que la tubería sale tan pronto comogrep
sale.Algunos ajustes menores:
tail
hace que comience a leer desde la última línea actual del archivo de registro, en caso de que la cadena exista antes en el archivo de registro.tail
-F en lugar de -f. No es POSIX, pero permitetail
trabajar incluso si el registro se gira mientras espera.grep
cierre después de la primera aparición, pero sin imprimir la línea de activación. También es POSIX, que -m1 no lo es.fuente
tail
ejecución en segundo plano para siempre. ¿Cómo capturaría eltail
PID dentro del subconjunto de fondo y lo expondría al shell principal? Solo puedo encontrar la solución subopcional eliminando todos lostail
procesos adjuntos de sesión , usandopkill -s 0 tail
.tail
terminará tan pronto como intente escribir en una tubería rota. La tubería se romperá tan pronto como segrep
haya completado, por lo que una vez que segrep
haya completado,tail
finalizará después de que el archivo de registro reciba una línea más.tail -f
.La respuesta aceptada no me funciona, además es confusa y cambia el archivo de registro.
Estoy usando algo como esto:
Si la línea de registro coincide con el patrón, elimine el
tail
iniciado por este script.Nota: si desea ver también la salida en la pantalla, haga
| tee /dev/tty
eco de la línea antes de probar en el bucle while.fuente
pkill
POSIX no lo especifica y no está disponible en todas partes.Si está usando Bash (al menos, pero parece que POSIX no lo define, por lo que puede faltar en algunos shells), puede usar la sintaxis
Funciona más o menos como las soluciones FIFO ya mencionadas, pero mucho más simple de escribir.
fuente
SIGTERM
(Ctrl + C, comando de salida o matarlo)tail
leerá, intentará generarlo y luego recibirá un SIGPIPE que lo terminará. Entonces, en principio tienes razón; eltail
podría ejecutarse indefinidamente si no se escribe nada en el archivo de registro nunca más. En la práctica, esta podría ser una solución muy ordenada para mucha gente.Hay algunas formas
tail
de salir:Enfoque deficiente: obligar
tail
a escribir otra líneaPuede forzar la
tail
escritura de otra línea de salida inmediatamente después degrep
encontrar una coincidencia y salir. Esto provocarátail
que aparezca unSIGPIPE
, haciendo que salga. Una forma de hacerlo es modificar el archivo que se está monitoreandotail
después de lasgrep
salidas.Aquí hay un código de ejemplo:
En este ejemplo,
cat
no saldrá hasta quegrep
haya cerrado su stdout, portail
lo que no es probable que pueda escribir en la tubería antes degrep
haber tenido la oportunidad de cerrar su stdin.cat
se utiliza para propagar la salida estándar degrep
no modificado.Este enfoque es relativamente simple, pero hay varias desventajas:
grep
cierra stdout antes de cerrar stdin, siempre habrá una condición de carrera:grep
cierra stdout, disparandocat
para salir, disparandoecho
, disparandotail
para generar una línea. Si esta línea se enviógrep
antes,grep
ha tenido la oportunidad de cerrar la entrada estándar,tail
no apareceráSIGPIPE
hasta que escriba otra línea.tail
no funcionará con otros programas.bash
laPIPESTATUS
matriz de 's ). Esto no es un gran problema en este caso porquegrep
siempre devolverá 0, pero en general la etapa intermedia podría ser reemplazada por un comando diferente cuyo código de retorno le interese (por ejemplo, algo que devuelve 0 cuando se detecta "servidor iniciado", 1 cuando se detecta "no se pudo iniciar el servidor").Los siguientes enfoques evitan estas limitaciones.
Un mejor enfoque: evitar tuberías
Puede usar un FIFO para evitar la canalización por completo, permitiendo que la ejecución continúe una vez que
grep
regrese. Por ejemplo:Las líneas marcadas con el comentario
# optional
pueden eliminarse y el programa seguirá funcionando;tail
solo permanecerá hasta que lea otra línea de entrada o sea eliminado por algún otro proceso.Las ventajas de este enfoque son:
tail
grep
(o cualquier comando alternativo que esté usando)La desventaja de este enfoque es la complejidad, especialmente la gestión de la FIFO: deberá generar de forma segura un nombre de archivo temporal, y deberá asegurarse de que la FIFO temporal se elimine incluso si el usuario presiona Ctrl-C en medio de la secuencia de comandos. Esto se puede hacer usando una trampa.
Enfoque alternativo: envíe un mensaje para matar
tail
Puede hacer que salga la
tail
etapa de canalización enviándole una señal comoSIGTERM
. El desafío es conocer de manera confiable dos cosas en el mismo lugar en el código:tail
el PID y sigrep
ha salido.Con una canalización como
tail -f ... | grep ...
, es fácil modificar la primera etapa de canalización para guardartail
el PID en una variable al fondotail
y leer$!
. También es fácil modificar la segunda etapa de canalización para que se ejecutekill
cuandogrep
salga. El problema es que las dos etapas de la tubería se ejecutan en "entornos de ejecución" separados (en la terminología del estándar POSIX) por lo que la segunda etapa de la tubería no puede leer ninguna variable establecida por la primera etapa de la tubería. Sin usar variables de shell, o bien la segunda etapa debe de alguna manera descubrirtail
el PID para que pueda matartail
cuandogrep
regrese, o la primera etapa debe ser notificada de alguna manera cuandogrep
regrese.La segunda etapa podría usarse
pgrep
para obtenertail
el PID, pero eso sería poco confiable (podría coincidir con el proceso incorrecto) y no portátil (pgrep
no está especificado por el estándar POSIX).La primera etapa podría enviar el PID a la segunda etapa a través de la tubería mediante
echo
el PID, pero esta cadena se mezclará contail
la salida de. La desmultiplexación de los dos puede requerir un esquema de escape complejo, dependiendo de la salida detail
.Puede usar un FIFO para que la segunda etapa de la tubería notifique a la primera etapa de la tubería cuando
grep
salga. Entonces la primera etapa puede matartail
. Aquí hay un código de ejemplo:Este enfoque tiene todos los pros y los contras del enfoque anterior, excepto que es más complicado.
Una advertencia sobre el almacenamiento en búfer
POSIX permite que las secuencias stdin y stdout se almacenen completamente en búfer, lo que significa que
tail
es posible que el resultado de la salida no se procesegrep
durante un tiempo arbitrariamente largo. No debería haber ningún problema en los sistemas GNU: GNUgrep
usaread()
, lo que evita todo almacenamiento en búfer, y GNUtail -f
realiza llamadas regularesfflush()
cuando escribe en stdout. Los sistemas que no son GNU pueden tener que hacer algo especial para deshabilitar o limpiar regularmente los búferes.fuente
tail -f
simplemente muestra los últimos diez líneas, y luego todo el siguiente. Para mejorar esto, puede agregar la opción-n 10000
a la cola para que también se distribuyan las últimas 10000 líneas.tail -f
a través de la FIFO y grepping en ella:mkfifo f; tail -f log > f & tailpid=$! ; grep -m 1 trigger f; kill $tailpid; rm f
.tail -f log
escrito en una FIFO hará que algunos sistemas (por ejemplo, GNU / Linux) utilicen el almacenamiento en búfer basado en bloques en lugar del almacenamiento en búfer basado en líneas, lo que significa quegrep
podría no ver la línea correspondiente cuando aparece en el registro. El sistema podría proporcionar una utilidad para cambiar el almacenamiento en búfer, como por ejemplostdbuf
de los núcleos de GNU. Sin embargo, dicha utilidad no sería portátil.grep -q -m 1 trigger <(tail -f log)
propuesto en otra parte y vivo con el hecho de que setail
ejecuta una línea más de fondo de lo necesario.Permítanme ampliar la respuesta de @ 00prometheus (que es la mejor).
Tal vez deberías usar un tiempo de espera en lugar de esperar indefinidamente.
La función bash a continuación se bloqueará hasta que aparezca el término de búsqueda dado o se alcance un tiempo de espera determinado.
El estado de salida será 0 si la cadena se encuentra dentro del tiempo de espera.
Quizás el archivo de registro aún no exista justo después de iniciar su servidor. En ese caso, debe esperar a que aparezca antes de buscar la cadena:
Así es como puedes usarlo:
fuente
timeout
comando?timeout
es la única forma confiable de no colgar indefinidamente esperando un servidor que no puede iniciarse y que ya se ha cerrado.Entonces, después de hacer algunas pruebas, encontré una forma rápida de 1 línea para hacer que esto funcione. Parece que tail -f se cerrará cuando grep se cierre, pero hay una trampa. Parece que solo se activa si el archivo se abre y se cierra. He logrado esto agregando la cadena vacía al archivo cuando grep encuentra la coincidencia.
No estoy seguro de por qué la apertura / cierre del archivo activa la cola para darse cuenta de que la tubería está cerrada, por lo que no confiaría en este comportamiento. pero parece funcionar por ahora.
Razón por la que se cierra, mira la bandera -F, versus la bandera -f.
fuente
tail
otra línea, pero para entonces segrep
ha cerrado (probablemente, hay una condición de carrera allí). Sigrep
ha salido para cuandotail
escribe otra línea,tail
obtendrá unSIGPIPE
. Eso hacetail
que salga de inmediato.tail
(6) no puede ajustarlo fácilmente para que se comporte de manera diferente dependiendo de las diferentes coincidencias de cadena ("servidor iniciado" frente a "error de inicio del servidor") porque no puede obtener fácilmente el código de retorno de la etapa media de la tubería. Existe un enfoque alternativo que evita todos estos problemas: vea mi respuesta.Actualmente, como se indica, todas las
tail -f
soluciones aquí corren el riesgo de elegir una línea "Iniciada por el servidor" registrada previamente (que puede o no ser un problema en su caso específico, dependiendo del número de líneas registradas y la rotación del archivo de registro / truncamiento).En lugar de complicar demasiado las cosas, solo use una más inteligente
tail
, como lo mostró bmike con un fragmento de Perl. La solución más simple es estaretail
que tiene soporte integrado de expresiones regulares con patrones de condición de inicio y parada :Esto seguirá el archivo de manera normal
tail -f
hasta que aparezca la primera nueva instancia de esa cadena, luego salga. (La-u
opción no se activa en las líneas existentes en las últimas 10 líneas del archivo cuando está en modo "seguir" normal).Si usa GNU
tail
(de coreutils ), la siguiente opción más simple es usar--pid
y un FIFO (canalización con nombre):Se utiliza un FIFO porque los procesos deben iniciarse por separado para obtener y pasar un PID. Un FIFO todavía sufre el mismo problema de esperar para una escritura oportuna
tail
para recibir un SIGPIPE , use la--pid
opción para quetail
salga cuando note quegrep
ha terminado (convencionalmente usado para monitorear el proceso del escritor en lugar del lector , perotail
no lo hace) Realmente me importa). La opción-n 0
se usa contail
para que las líneas antiguas no activen una coincidencia.Finalmente, puede usar una cola con estado , esto almacenará el desplazamiento del archivo actual para que las invocaciones posteriores solo muestren nuevas líneas (también maneja la rotación del archivo). Este ejemplo usa el antiguo FWTK
retail
*:* Nota, mismo nombre, programa diferente a la opción anterior.
En lugar de tener un bucle de CPU, compare la marca de tiempo del archivo con el archivo de estado (
.${LOGFILE}.off
) y duerma. Use "-T
" para especificar la ubicación del archivo de estado si es necesario, lo anterior asume el directorio actual. Siéntase libre de omitir esa condición, o en Linux podría usar el más eficiente en suinotifywait
lugar:fuente
retail
con un tiempo de espera, como: "Si han pasado 120 segundos y el comercio aún no ha leído la línea, entonces dar un código de error y salir del comercio minorista"?timeout
(coreutils) para el lanzamientoretail
y justo para comprobar el código de salida 124 en tiempo de espera (timeout
matará a cualquier comando que se utiliza para iniciar después de la hora fijada)Esto será un poco complicado ya que tendrá que entrar en el control del proceso y la señalización. Más kludgey sería una solución de dos scripts que utiliza el seguimiento PID. Mejor estaría usando tuberías con nombre como este.
¿Qué script de shell estás usando?
Para una solución rápida y sucia, una secuencia de comandos: crearía una secuencia de comandos perl usando File: Tail
Por lo tanto, en lugar de imprimir dentro del bucle while, puede filtrar la coincidencia de la cadena y salir del bucle while para permitir que su script continúe.
Cualquiera de estos debe implicar un poco de aprendizaje para implementar el control de flujo de vigilancia que está buscando.
fuente
maxinterval=>300
significa que verificará el archivo cada cinco minutos. Como sé que mi línea aparecerá momentáneamente en el archivo, estoy usando encuestas mucho más agresivas:maxinterval=>0.2, adjustafter=>10000
espera a que aparezca el archivo
esperar a que la cadena aparezca en el archivo
https://superuser.com/a/743693/129669
fuente
/path/to/the.file
que es 1.4GB grande; entonces está claro que esto es un problema. 2. Espera más de lo necesario cuando aparece la entrada del registro, en el peor de los casos 10s.No puedo imaginar una solución más limpia que esta:
ok, tal vez el nombre puede estar sujeto a mejoras ...
Ventajas:
fuente
No es necesario que necesites cola para hacer eso. Creo que el comando de reloj es lo que estás buscando. El comando watch monitorea la salida de un archivo y puede terminarse con la opción -g cuando la salida cambia.
fuente
Alex, creo que este te ayudará mucho.
este comando nunca dará una entrada en el archivo de registro pero grep en silencio ...
fuente
logfile
contrario, podría pasar un tiempo arbitrariamente largo antes de quetail
salga otra línea y detecte quegrep
ha muerto (víaSIGPIPE
).Aquí hay una solución mucho mejor que no requiere que escriba en el archivo de registro, lo cual es muy peligroso o incluso imposible en algunos casos.
Actualmente solo tiene un efecto secundario, el
tail
proceso permanecerá en segundo plano hasta que se escriba la siguiente línea en el registro.fuente
tail -n +0 -f
comienza desde el principio del archivo.tail -n 0 -f
comienza desde el final del archivo.myscript.sh: line 14: 7845 Terminated sh -c 'tail...
tail
proceso sigue ejecutándose en segundo plano.Las otras soluciones aquí tienen varios problemas:
Esto es lo que se me ocurrió usando tomcat como ejemplo (elimine los hash si desea ver el registro mientras se inicia):
fuente
El
tail
comando puede estar en segundo plano y su pid se repite en lagrep
subshell. En lagrep
subshell, un controlador de trampa en EXIT puede matar eltail
comando.fuente
Léelos todos. tldr: desacoplar la terminación de la cola de grep.
Las dos formas más convenientes son
y si tienes bash
Pero si esa cola sentada en el fondo te molesta, hay una mejor manera que una respuesta de quince o cualquier otra aquí. Requiere bash.
O si no es la cola la que está produciendo cosas,
fuente
Intenta usar inotify (inotifywait)
Configure inotifywait para cualquier cambio de archivo, luego verifique el archivo con grep, si no lo encuentra, simplemente vuelva a ejecutar inotifywait, si lo encuentra, salga del bucle ... algo así
fuente
$!
; inotifywait -e MODIFICAR / tmp / encontrado; kill -KILL - $ MYPIDDesea irse tan pronto como se escriba la línea, pero también quiere irse después de un tiempo de espera:
fuente
Qué tal esto:
si bien es cierto; Hacer si [ ! -z $ (grep "myRegEx" myLog.log)]; luego descanso; fi; hecho
fuente