Estou usando o tail -f para monitorar um arquivo de log que está sendo gravado ativamente. Quando uma determinada sequência de caracteres é gravada no arquivo de log, desejo encerrar o monitoramento e continuar com o restante do meu script.
Atualmente estou usando:
tail -f logfile.log | grep -m 1 "Server Started"
Quando a string é encontrada, o grep é encerrado conforme o esperado, mas preciso encontrar uma maneira de fazer com que o comando tail seja fechado também para que o script possa continuar.
tail
morre apenas na próxima linha. Tente isto:date > log; tail -f log | grep -m 1 trigger
e depois em outro shell:echo trigger >> log
e você verá a saídatrigger
no primeiro shell, mas sem o término do comando. Em seguida, tente:date >> log
no segundo shell e o comando no primeiro shell será encerrado. Mas às vezes isso é tarde demais; queremos terminar assim que a linha de disparo aparecer, não quando a linha após a linha de disparo estiver concluída.tail
+grep -q
como resposta de 00prometheusRespostas:
Um simples liner POSIX
Aqui está uma linha simples. Ele não precisa de truques específicos para o bash ou não POSIX, nem mesmo um pipe nomeado. Tudo o que você realmente precisa é desacoplar o término de
tail
fromgrep
. Dessa forma, assim quegrep
terminar, o script poderá continuar mesmo setail
ainda não tiver terminado. Portanto, esse método simples o levará até lá:grep
irá bloquear até encontrar a string e, em seguida, sairá. Aotail
executar a partir de seu próprio sub-shell, podemos colocá-lo em segundo plano para que seja executado de forma independente. Enquanto isso, o shell principal está livre para continuar a execução do script assim quegrep
sair.tail
permanecerá em seu sub-shell até que a próxima linha seja gravada no arquivo de log e saia (possivelmente mesmo depois que o script principal for finalizado). O ponto principal é que o pipeline não espera maistail
terminar, portanto o pipeline sai assim quegrep
sai.Alguns pequenos ajustes:
tail
iniciar a leitura da última linha atual do arquivo de log, caso a sequência exista anteriormente no arquivo de log.tail
-F ao invés de -f. Não é POSIX, mas permitetail
trabalhar mesmo se o log for girado enquanto aguarda.grep
sai após a primeira ocorrência, mas sem imprimir a linha de acionamento. Também é POSIX, o que -m1 não é.fonte
tail
execução em segundo plano para sempre. Como você capturaria otail
PID dentro do sub-shell em segundo plano e o exporia como o shell principal? Só posso apresentar a solução alternativa sub-opcional matando todos ostail
processos anexados à sessão , usandopkill -s 0 tail
.tail
terminará assim que tentar gravar em um tubo quebrado. O canal será quebrado assim quegrep
for concluído, portanto, uma vezgrep
concluído,tail
será encerrado após o arquivo de log ter mais uma linha nele.tail -f
.A resposta aceita não está funcionando para mim, além de ser confusa e alterar o arquivo de log.
Estou usando algo parecido com isto:
Se a linha de log corresponder ao padrão, mate o
tail
iniciado por este script.Nota: se você também quiser visualizar a saída na tela, faça
| tee /dev/tty
eco na linha antes de testar no loop while.fonte
pkill
não é especificado pelo POSIX e não está disponível em qualquer lugar.Se você estiver usando o Bash (pelo menos, mas parece que não está definido pelo POSIX, pode estar ausente em alguns shells), você pode usar a sintaxe
Funciona praticamente como as soluções FIFO já mencionadas, mas muito mais simples de escrever.
fonte
SIGTERM
(Ctrl + C, comando de saída, ou matá-lo)tail
lerá, tentará produzi-lo e, em seguida, receberá um SIGPIPE que o encerrará. Então, em princípio, você está certo; otail
pode funcionar indefinidamente, se nada é gravada no arquivo de log nunca mais. Na prática, isso pode ser uma solução muito interessante para muitas pessoas.Existem algumas maneiras
tail
de sair:Má abordagem: forçar
tail
a escrever outra linhaVocê pode forçar
tail
a escrever outra linha de saída imediatamente apósgrep
encontrar uma correspondência e sair. Isso fará comtail
que obtenha umSIGPIPE
, fazendo com que ele saia. Uma maneira de fazer isso é modificar o arquivo que está sendo monitoradotail
após asgrep
saídas.Aqui está um exemplo de código:
Neste exemplo,
cat
não sairá atégrep
que o stdout seja fechado, portanto,tail
é provável que você não consiga gravar no pipe antes degrep
ter a chance de fechar o stdin.cat
é usado para propagar a saída padrão degrep
não modificado.Essa abordagem é relativamente simples, mas existem várias desvantagens:
grep
fechar stdout antes de fechar stdin, sempre haverá uma condição de corrida:grep
fecha stdout, acionandocat
para sair, acionandoecho
, acionandotail
para emitir uma linha. Se essa linha é enviada paragrep
antesgrep
de fechar o stdin,tail
não será exibidaSIGPIPE
até que ela escreva outra linha.tail
- não funcionará com outros programas.bash
aPIPESTATUS
matriz de). Isso não é muito importante nesse caso, porquegrep
sempre retornará 0, mas, em geral, o estágio intermediário pode ser substituído por um comando diferente cujo código de retorno você se preocupa (por exemplo, algo que retorne 0 quando o "servidor iniciado" for detectado, 1 quando "servidor falhou ao iniciar" for detectado).As próximas abordagens evitam essas limitações.
Uma abordagem melhor: evitar tubulações
Você pode usar um FIFO para evitar completamente o pipeline, permitindo que a execução continue assim que o
grep
retorno for retornado. Por exemplo:As linhas marcadas com o comentário
# optional
podem ser removidas e o programa ainda funcionará;tail
permanecerá apenas até que leia outra linha de entrada ou seja morto por algum outro processo.As vantagens dessa abordagem são:
tail
grep
(ou qualquer comando alternativo que esteja usando)A desvantagem dessa abordagem é a complexidade, especialmente o gerenciamento do FIFO: você precisará gerar com segurança um nome de arquivo temporário e garantir que o FIFO temporário seja excluído, mesmo que o usuário pressione Ctrl-C no meio de o script. Isso pode ser feito usando uma armadilha.
Abordagem alternativa: envie uma mensagem para matar
tail
Você pode obter o
tail
estágio do pipeline para sair enviando um sinal comoSIGTERM
. O desafio é saber com segurança duas coisas no mesmo local no código:tail
o PID e segrep
foi encerrado.Com um pipeline como
tail -f ... | grep ...
esse, é fácil modificar o primeiro estágio do pipeline para salvartail
o PID de uma variável em segundo planotail
e lendo$!
. Também é fácil modificar o segundo estágio do pipeline para executarkill
quandogrep
sair. O problema é que os dois estágios do pipeline são executados em "ambientes de execução" separados (na terminologia do padrão POSIX), portanto, o segundo estágio do pipeline não pode ler nenhuma variável definida pelo primeiro estágio do pipeline. Sem o uso de variáveis de shell, o segundo estágio deve, de alguma forma, descobrir otail
PID para que ele possa matartail
quandogrep
retorna, ou o primeiro estágio deve ser notificado de alguma forma quandogrep
retorna.O segundo estágio pode ser usado
pgrep
para obtertail
o PID, mas isso não é confiável (você pode corresponder ao processo errado) e nãopgrep
é portátil ( não é especificado pelo padrão POSIX).O primeiro estágio pode enviar o PID para o segundo estágio através do pipe, usando
echo
o PID, mas essa string será misturada comtail
a saída. A desmultiplexação dos dois pode exigir um esquema de escape complexo, dependendo da saída detail
.Você pode usar um FIFO para que o segundo estágio do pipeline notifique o primeiro estágio do pipeline quando
grep
sair. Então o primeiro estágio pode matartail
. Aqui está um exemplo de código:Essa abordagem tem todos os prós e contras da abordagem anterior, exceto que é mais complicada.
Um aviso sobre buffer
O POSIX permite que os fluxos stdin e stdout sejam totalmente armazenados em buffer, o que significa que
tail
a saída pode não ser processada porgrep
um tempo arbitrariamente longo. Não deve haver nenhum problema nos sistemas GNU: o GNUgrep
usaread()
, o que evita todos os buffers, e o GNUtail -f
faz chamadas regulares aofflush()
gravar no stdout. Os sistemas não-GNU podem precisar fazer algo especial para desativar ou liberar regularmente os buffers.fonte
tail -f
produzirá apenas as últimas dez linhas e, a seguir, todas as seguintes. Para melhorar isso, você pode adicionar a opção-n 10000
à cauda, para que as últimas 10000 linhas também sejam fornecidas.tail -f
meio do FIFO e grepping sobre ele:mkfifo f; tail -f log > f & tailpid=$! ; grep -m 1 trigger f; kill $tailpid; rm f
.tail -f log
gravar em um FIFO fará com que alguns sistemas (por exemplo, GNU / Linux) usem buffer baseado em bloco em vez de buffer baseado em linha, o que significa quegrep
talvez não veja a linha correspondente quando aparece no log. O sistema pode fornecer um utilitário para alterar o buffer, comostdbuf
no GNU coreutils. Esse utilitário não seria portátil, no entanto.grep -q -m 1 trigger <(tail -f log)
proposta ainda mais simples em outro lugar e convido com o fato de que atail
linha é executada uma vez mais em segundo plano do que o necessário.Deixe-me expandir na resposta do 00prometheus (qual é a melhor).
Talvez você deva usar um tempo limite em vez de esperar indefinidamente.
A função bash abaixo será bloqueada até que o termo de pesquisa especificado apareça ou que um tempo limite seja atingido.
O status de saída será 0 se a sequência for encontrada dentro do tempo limite.
Talvez o arquivo de log ainda não exista logo após o lançamento do servidor. Nesse caso, você deve esperar que ele apareça antes de procurar a string:
Veja como você pode usá-lo:
fonte
timeout
comando?timeout
é a única maneira confiável de não travar indefinidamente, esperando por um servidor que não pode ser iniciado e já saiu.Então, depois de fazer alguns testes, encontrei uma maneira rápida de 1 linha para fazer isso funcionar. Parece que tail -f será encerrado quando o grep for encerrado, mas há um problema. Parece ser acionado apenas se o arquivo for aberto e fechado. Eu consegui isso anexando a string vazia ao arquivo quando grep encontra a correspondência.
Não sei por que o abrir / fechar o arquivo aciona a cauda para perceber que o canal está fechado, portanto, não confio nesse comportamento. mas parece funcionar por enquanto.
Razão pelo qual fecha, observe o sinalizador -F versus o sinalizador -f.
fonte
tail
a saída de outra linha, masgrep
já saiu (provavelmente - há uma condição de corrida). Segrep
tiver terminado quandotail
escrever outra linha,tail
receberá aSIGPIPE
. Isso faztail
com que saia imediatamente.tail
(6) você não pode ajustá-lo facilmente para se comportar de maneira diferente, dependendo de diferentes correspondências de strings ("servidor iniciado" vs. "falha no início do servidor") porque você não pode obter facilmente o código de retorno do estágio intermediário do pipeline. Existe uma abordagem alternativa que evita todos esses problemas - veja minha resposta.Atualmente, como indicado, todas as
tail -f
soluções aqui correm o risco de selecionar uma linha "Iniciada pelo servidor" registrada anteriormente (o que pode ou não ser um problema no seu caso específico, dependendo do número de linhas registradas e da rotação do arquivo de log / truncamento).Em vez de complicar demais as coisas, use apenas uma inteligência
tail
, como bmike mostrou com um snippit perl. A solução mais simples é aretail
que possui suporte a regex integrado com padrões de condição de início e parada :Isso seguirá o arquivo normalmente
tail -f
até que a primeira nova instância dessa sequência seja exibida e saia. (A-u
opção não dispara nas linhas existentes nas últimas 10 linhas do arquivo quando no modo "seguir" normal).Se você usa o GNU
tail
(do coreutils ), a próxima opção mais simples é usar--pid
e um FIFO (pipe nomeado):Um FIFO é usado porque os processos devem ser iniciados separadamente para obter e passar um PID. A FIFO ainda sofre com o mesmo problema de pendurar em torno de uma gravação oportuna a causa
tail
para receber um SIGPIPE , use a--pid
opção para quetail
as saídas quando se percebe quegrep
foi terminado (convencionalmente usado para monitorar o escritor processo em vez do leitor , mastail
doesn' realmente não me importo). A opção-n 0
é usada comtail
para que as linhas antigas não disparem uma correspondência.Por fim, você pode usar uma cauda com estado , isso armazenará o deslocamento do arquivo atual para que as chamadas subsequentes mostrem apenas novas linhas (ele também lida com a rotação do arquivo). Este exemplo usa o antigo FWTK
retail
*:* Observe, mesmo nome, programa diferente da opção anterior.
Em vez de ter um loop de sobrecarga da CPU, compare o registro de data e hora do arquivo com o arquivo de estado (
.${LOGFILE}.off
) e durma. Use "-T
" para especificar o local do arquivo de estado, se necessário, o que precede assume o diretório atual. Sinta-se à vontade para ignorar essa condição ou, no Linux, você pode usar o mais eficienteinotifywait
:fonte
retail
com um tempo limite, como: "Se 120 segundos se passaram e o varejo ainda não leu a linha, forneça um código de erro e saia do varejo"?timeout
(coreutils) para lançamentoretail
e apenas verificar se há código de saída 124 em timeout (timeout
matará qualquer comando que você usá-lo para iniciar após o tempo definido)Isso será um pouco complicado, pois você terá que entrar no controle e sinalização do processo. Mais kludgey seria uma solução de dois scripts usando o rastreamento PID. Melhor seria usar pipes nomeados como este.
Qual script de shell você está usando?
Para uma solução rápida e suja, um script - eu faria um script perl usando File: Tail
Portanto, em vez de imprimir dentro do loop while, você pode filtrar a correspondência de sequência e interromper o loop while para permitir que o script continue.
Qualquer um destes deve envolver apenas um pouco de aprendizado para implementar o controle de fluxo de observação que você está procurando.
fonte
maxinterval=>300
significa que ele verificará o arquivo a cada cinco minutos. Como sei que minha linha aparecerá no arquivo momentaneamente, estou usando uma pesquisa muito mais agressiva:maxinterval=>0.2, adjustafter=>10000
aguarde o arquivo aparecer
aguarde a seqüência de caracteres apper no arquivo
https://superuser.com/a/743693/129669
fonte
/path/to/the.file
tamanho de 1,4 GB; então fica claro que isso é um problema. 2. Aguarda mais tempo do que o necessário quando a entrada do log é exibida, no pior dos casos, 10s.Não consigo imaginar uma solução mais limpa que esta:
ok, talvez o nome possa estar sujeito a melhorias ...
Vantagens:
fonte
Você não precisa de cauda para fazer isso. Eu acho que o comando watch é o que você está procurando. O comando watch monitora a saída de um arquivo e pode ser finalizado com a opção -g quando a saída foi alterada.
fonte
Alex, acho que este irá ajudá-lo muito.
este comando nunca fornecerá uma entrada no arquivo de log, mas cumprimentará silenciosamente ...
fonte
logfile
lo, caso contrário, poderá levar um tempo arbitrariamente longo antes detail
emitir outra linha e detectar quegrep
morreu (viaSIGPIPE
).Aqui está uma solução muito melhor que não exige que você escreva no arquivo de log, o que é muito perigoso ou mesmo impossível em alguns casos.
Atualmente, ele tem apenas um efeito colateral, o
tail
processo permanecerá em segundo plano até que a próxima linha seja gravada no log.fonte
tail -n +0 -f
começa a partir do início do arquivo.tail -n 0 -f
começa no final do arquivo.myscript.sh: line 14: 7845 Terminated sh -c 'tail...
tail
processo continua sendo executado em segundo plano.As outras soluções aqui têm vários problemas:
Aqui está o que eu descobri usando o tomcat como exemplo (remova os hashes se você quiser ver o log enquanto ele é iniciado):
fonte
O
tail
comando pode ser em segundo plano e seu pid ecoou nogrep
subshell. Nogrep
subshell, um manipulador de trap no EXIT pode matar otail
comando.fonte
Leia todos eles. tldr: desacopla a terminação da cauda do grep.
As duas formas mais convenientes são
e se você tiver festança
Mas se a cauda em segundo plano o incomoda, há uma maneira mais agradável do que uma resposta quino ou qualquer outra aqui. Requer festança.
Ou se não é a cauda que está produzindo coisas,
fonte
Tente usar inotify (inotifywait)
Você configura o inotifywait para qualquer alteração no arquivo e, em seguida, verifique o arquivo com grep, se não encontrado, execute novamente o inotifywait, se encontrado sair do loop ...
fonte
$!
; inotifywait -e MODIFY / tmp / encontrado; kill -KILL - $ MYPIDVocê deseja sair assim que a linha for gravada, mas também deseja sair após um tempo limite:
fonte
que tal agora:
enquanto verdadeiro; Faça se [ ! -z $ (grep "myRegEx" myLog.log)]; então quebre; fi; feito
fonte