Para aqueles que têm um programa que está continuamente escrevendo para stdout, tudo que você precisa fazer é canalizá-lo para grep com a opção 'single match'. Uma vez que o grep encontre a string correspondente, ele sairá, o que fecha o stdout no processo que está sendo canalizado para o grep. Este evento deve naturalmente fazer com que o programa saia normalmente desde que o processo seja gravado novamente .
O que acontecerá é que o processo receberá um SIGPIPE quando tentar gravar em stdout fechado após o grep ter saído. Aqui está um exemplo com o ping, que, de outra forma, seria executado indefinidamente:
$ ping superuser.com | grep -m 1 "icmp_seq"
Este comando irá coincidir com o primeiro 'pong' bem-sucedido e, em seguida, sairá da próxima vez que ping
tentar escrever para stdout.
No entanto,
Nem sempre é garantido que o processo irá gravar no stdout novamente e, portanto, pode não fazer com que um SIGPIPE seja gerado (por exemplo, isso pode acontecer ao rastrear um arquivo de log). A melhor solução que consegui criar para esse cenário envolve gravar em um arquivo; por favor, comente se você acha que pode melhorar:
$ { tail -f log_file & echo $! > pid; } | { grep -m1 "find_me" && kill -9 $(cat pid) && rm pid; }
Quebrando isso:
-
tail -f log_file & echo $! > pid
- coroa um arquivo, anexa o processo ao segundo plano e salva o PID ( $!
) em um arquivo. Eu tentei exportar o PID para uma variável, mas parece que há uma condição de corrida entre aqui e quando o PID é usado novamente.
-
{ ... ;}
- agrupe estes comandos juntos para que possamos canalizar a saída para grep mantendo o contexto atual (ajuda ao salvar e reutilizar variáveis, mas não conseguiu fazer com que a parte funcione)
-
|
- canaliza o stdout do lado esquerdo para o stdin do lado direito
-
grep -m1 "find_me"
- encontre a string de destino
-
&& kill -9 $(cat pid)
- forçar kill (SIGKILL) o tail
process após grep
sair assim que encontrar a string correspondente
-
&& rm pid
- remova o arquivo que criamos