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.

taildados solo en la siguiente línea. Intente esto:date > log; tail -f log | grep -m 1 triggery luego en otro shell:echo trigger >> logy verá la salidatriggeren el primer shell, pero no termina el comando. Luego intente:date >> logen 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 -qcomo 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
tailfromgrep. De esa manera, una vez quegrepfinaliza, el script puede continuar incluso sitailaún no ha finalizado. Entonces, este método simple lo llevará allí:grepse bloqueará hasta que haya encontrado la cadena, con lo que saldrá. Altailejecutar 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 comogrepsalga.tailpermanecerá 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 atailque termine, por lo que la tubería sale tan pronto comogrepsale.Algunos ajustes menores:
tailhace 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 permitetailtrabajar incluso si el registro se gira mientras espera.grepcierre 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
tailejecución en segundo plano para siempre. ¿Cómo capturaría eltailPID dentro del subconjunto de fondo y lo expondría al shell principal? Solo puedo encontrar la solución subopcional eliminando todos lostailprocesos adjuntos de sesión , usandopkill -s 0 tail.tailterminará tan pronto como intente escribir en una tubería rota. La tubería se romperá tan pronto como segrephaya completado, por lo que una vez que segrephaya completado,tailfinalizará 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
tailiniciado por este script.Nota: si desea ver también la salida en la pantalla, haga
| tee /dev/ttyeco de la línea antes de probar en el bucle while.fuente
pkillPOSIX 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)tailleerá, intentará generarlo y luego recibirá un SIGPIPE que lo terminará. Entonces, en principio tienes razón; eltailpodrí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
tailde salir:Enfoque deficiente: obligar
taila escribir otra líneaPuede forzar la
tailescritura de otra línea de salida inmediatamente después degrepencontrar una coincidencia y salir. Esto provocarátailque aparezca unSIGPIPE, haciendo que salga. Una forma de hacerlo es modificar el archivo que se está monitoreandotaildespués de lasgrepsalidas.Aquí hay un código de ejemplo:
En este ejemplo,
catno saldrá hasta quegrephaya cerrado su stdout, portaillo que no es probable que pueda escribir en la tubería antes degrephaber tenido la oportunidad de cerrar su stdin.catse utiliza para propagar la salida estándar degrepno modificado.Este enfoque es relativamente simple, pero hay varias desventajas:
grepcierra stdout antes de cerrar stdin, siempre habrá una condición de carrera:grepcierra stdout, disparandocatpara salir, disparandoecho, disparandotailpara generar una línea. Si esta línea se enviógrepantes,grepha tenido la oportunidad de cerrar la entrada estándar,tailno apareceráSIGPIPEhasta que escriba otra línea.tailno funcionará con otros programas.bashlaPIPESTATUSmatriz de 's ). Esto no es un gran problema en este caso porquegrepsiempre 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
grepregrese. Por ejemplo:Las líneas marcadas con el comentario
# optionalpueden eliminarse y el programa seguirá funcionando;tailsolo permanecerá hasta que lea otra línea de entrada o sea eliminado por algún otro proceso.Las ventajas de este enfoque son:
tailgrep(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
tailPuede hacer que salga la
tailetapa 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:tailel PID y sigrepha salido.Con una canalización como
tail -f ... | grep ..., es fácil modificar la primera etapa de canalización para guardartailel PID en una variable al fondotaily leer$!. También es fácil modificar la segunda etapa de canalización para que se ejecutekillcuandogrepsalga. 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 descubrirtailel PID para que pueda matartailcuandogrepregrese, o la primera etapa debe ser notificada de alguna manera cuandogrepregrese.La segunda etapa podría usarse
pgreppara obtenertailel PID, pero eso sería poco confiable (podría coincidir con el proceso incorrecto) y no portátil (pgrepno 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
echoel PID, pero esta cadena se mezclará contailla 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
grepsalga. 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
tailes posible que el resultado de la salida no se procesegrepdurante un tiempo arbitrariamente largo. No debería haber ningún problema en los sistemas GNU: GNUgrepusaread(), lo que evita todo almacenamiento en búfer, y GNUtail -frealiza 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 -fsimplemente muestra los últimos diez líneas, y luego todo el siguiente. Para mejorar esto, puede agregar la opción-n 10000a la cola para que también se distribuyan las últimas 10000 líneas.tail -fa 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 logescrito 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 quegreppodrí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 ejemplostdbufde 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 setailejecuta 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
timeoutcomando?timeoutes 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
tailotra línea, pero para entonces segrepha cerrado (probablemente, hay una condición de carrera allí). Sigrepha salido para cuandotailescribe otra línea,tailobtendrá unSIGPIPE. Eso hacetailque 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 -fsoluciones 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 estaretailque tiene soporte integrado de expresiones regulares con patrones de condición de inicio y parada :Esto seguirá el archivo de manera normal
tail -fhasta que aparezca la primera nueva instancia de esa cadena, luego salga. (La-uopció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--pidy 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
tailpara recibir un SIGPIPE , use la--pidopción para quetailsalga cuando note quegrepha terminado (convencionalmente usado para monitorear el proceso del escritor en lugar del lector , perotailno lo hace) Realmente me importa). La opción-n 0se usa contailpara 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 suinotifywaitlugar:fuente
retailcon 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 lanzamientoretaily justo para comprobar el código de salida 124 en tiempo de espera (timeoutmatará 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=>300significa 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=>10000espera a que aparezca el archivo
esperar a que la cadena aparezca en el archivo
https://superuser.com/a/743693/129669
fuente
/path/to/the.fileque 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
logfilecontrario, podría pasar un tiempo arbitrariamente largo antes de quetailsalga otra línea y detecte quegrepha 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
tailproceso permanecerá en segundo plano hasta que se escriba la siguiente línea en el registro.fuente
tail -n +0 -fcomienza desde el principio del archivo.tail -n 0 -fcomienza desde el final del archivo.myscript.sh: line 14: 7845 Terminated sh -c 'tail...tailproceso 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
tailcomando puede estar en segundo plano y su pid se repite en lagrepsubshell. En lagrepsubshell, un controlador de trampa en EXIT puede matar eltailcomando.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