Como ler uma linha de 'tail -f' através de um pipeline, e depois terminar?

7

Eu gostaria de seguir as alterações em um arquivo via tail -f e, em seguida, imprimir a primeira linha que corresponda a um comando grep antes de finalizar. Qual é a maneira mais simples de fazer isso? Até agora eu tenho experimentado coisas como:

tail -f foo.log | grep 'bar' | head -1

No entanto, o pipeline apenas trava, presumivelmente devido ao buffering. Eu também tentei

tail -f foo.log | grep --line-buffered 'bar' | head -1

Isto imprime a linha, mas o processo não termina a menos que eu pressione ^ C, presumivelmente porque uma segunda linha de entrada é necessária para terminar head -1 . Qual é a melhor maneira de resolver este problema?

    
por jonderry 27.04.2011 / 03:02

5 respostas

10
tail -f foo.log | grep -m 1 bar

se o arquivo foo.log for escrito para, você pode fazer:

grep -m 1 bar <( tail -f foo.log )

Deve-se notar que a cauda -f permanecerá em segundo plano até obter outra linha para saída. Se isso levar muito tempo, pode ser problemático. solução em tal caso é:

grep -m 1 bar <( exec tail -f foo.log ); kill $! 2> /dev/null

kill matará o processo tail-f restante, e nós ocultamos os erros, porque é possível que o tail tenha sumido quando o kill for invocado.

    
por 27.04.2011 / 12:37
1

tail -f esperando por SIGPIPE , que, como já foi apontado por depesz, só pode ser acionado por outra gravação de tail -f em um canal fechado após uma coincidência grep -m 1 bem-sucedida, pode ser evitada se a% O comandotail -f fica em segundo plano e seu pid de segundo plano é enviado para o subgrupo grep , que por sua vez implementa uma trap na saída que mata tail -f explicitamente.

#(tail -f foo.log & echo ${!} ) |
(tail -f foo.log & echo ${!} && wait ) | 
   (trap 'kill "$tailpid"; trap - EXIT' EXIT; tailpid="$(head -1)"; grep -m 1 bar)
    
por 17.04.2013 / 17:20
1

A construção < () funciona apenas no bash. Minha solução é:

sleep $timeout &
pid=$!
tail -n +0 --pid=$pid -F "$file" | ( grep -q -m 1 "$regexp" && kill -PIPE $pid )
wait $pid

Além de estar trabalhando em um shell POSIX, uma outra vantagem é que o período de espera pode ser limitado com um valor de tempo limite. Observe que o código de resultado deste scriptlet é invertido: 0 significa que o tempo limite foi atingido.

    
por 07.06.2013 / 13:15
0

Com base na resposta link , percebi que, se o tail fosse iniciado tarde demais, alguma instância do indicado string já passou. A solução é fazer a lista completa do arquivo inteiro.

grep -m 1 bar <( exec tail -n +1 -f foo.log ); kill $!
    
por 02.01.2013 / 10:39
-1

Acho que aplicar unbuffer assim,

tail -f foo.log | unbuffer -p grep 'bar' | head -1

funcionaria. Eu não estou em um sistema onde posso testá-lo, e não posso justificar por que funcionaria enquanto grep --line-buffered não funciona. No entanto, tentei esta alternativa que parece funcionar:

tail -f foo.log | sed -n '/bar/{p;q}'

Quando a correspondência para bar for encontrada, p imprime e q sai imediatamente.

    
por 27.04.2011 / 08:42

Tags