Come leggere una row da "tail -f" attraverso una pipeline e poi terminare?

Vorrei seguire le modifiche a un file tramite la tail -f e quindi printingre la prima row che corrisponde a un command grep prima di terminare. Qual è il modo più semplice per farlo? Finora ho sperimentato cose come:

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

Tuttavia, la pipeline appende appena, presumibilmente a causa del buffering. Ho anche provato

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

Questo printing la linea, ma il process non termina se non ho colpito ^ C, presumibilmente perché una seconda row di input è necessaria per terminare la head -1 . Qual è il modo migliore per risolvere questo problema?

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

se il file foo.log viene scritto in modo random, è ansible:

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

Va notato che la coda -f resterà in background finché non otterrà un'altra linea per l'output. Se questo richiede molto tempo potrebbe essere problematico. soluzione in questo caso è:

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

uccidere ucciderà il process di coda di rimanente -f, e nascondiamo gli errori, perché è ansible che la coda sia scomparsa per il momento in cui verrà ucciso.

tail -f attesa di SIGPIPE che, come già sottolineato da depesz, può essere triggersto solo da un'altra scrittura di tail -f ad un pipe chiuso dopo un successo grep -m 1 match, può essere evitato se il command tail -f in background e il suo background viene inviato al grep subshell, che a sua volta realizza una trap all'output che uccide il tail -f esplicitamente.

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

Il costrutto <() funziona solo in bash. La mia soluzione è:

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

Oltre a funzionare in una shell POSIX, un altro vantaggio è che il periodo di attesa può essere limitato con un valore di timeout. Si noti che il codice di risultato di questo script è invertito: 0 significa che è stato raggiunto il timeout.

Basandosi sulla risposta https://superuser.com/a/275962/37154 , ho capito che se la tail inizierebbe troppo tardi, qualche istanza della string indicata potrebbe essere già andata. La soluzione è quella di rendere l'elenco di coda l'integer file.

 grep -m 1 bar <( exec tail -n +1 -f foo.log ); kill $! 

Penso che l'applicazione di unbuffer come questo,

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

functionrebbe. Non sono in un sistema in cui posso provarlo, e non posso giustificare perché functionrebbe mentre grep --line-buffered non lo fa. Tuttavia, ho provato questa alternativa che sembra funzionare:

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

Quando viene trovata la corrispondenza alla bar p printing e q si chiude immediatamente.