Come 'grep' un flusso continuo?


729

È possibile utilizzarlo grepsu un flusso continuo?

Quello che voglio dire è una specie di tail -f <file>comando, ma con grepl'output per mantenere solo le linee che mi interessano.

Ci ho provato tail -f <file> | grep patternma sembra che greppossa essere eseguito solo una volta tailterminato, vale a dire mai.


9
È molto probabile che il programma che genera il file non stia scaricando il suo output.
Steve-o

tail -f filefunziona (vedo la nuova uscita in tempo reale)
Matthieu Napoli,

6
Sarebbe appropriato per unix.stackexchange.com
Luc M

@Luc, in effetti, non ci ho pensato
Matthieu Napoli,

Potrebbe non esserci nuove righe nel tuo flusso di input? In tal caso grep non procederà.
Lynch,

Risposte:


1328

Attiva la grepmodalità buffering di linea quando usi BSD grep (FreeBSD, Mac OS X ecc.)

tail -f file | grep --line-buffered my_pattern

Non è necessario eseguire questa operazione per GNU grep (utilizzato praticamente su qualsiasi Linux) poiché verrà scaricato per impostazione predefinita (YMMV per altri Mi piace di Unix come SmartOS, AIX o QNX).


3
@MichaelNiemand potresti usare il file tail -F | grep --line-buffered my_pattern
jcfrei

47
@MichaelGoldshteyn Vacci piano. Le persone lo votano perché trovano questa pagina quando google "grep line buffered" e risolve un problema per loro che potrebbe non essere esattamente quello posto come la domanda.
raine,

4
Sono venuto qui cercando di ottenere l'output di strace. Senza il --line-buffered, non funzionerà.
sjas,

5
@MichaelGoldshteyn (e le upvoters del suo commento): ho sempre avuto questo problema con tail -f | grep, e --line-bufferedrisolve per me (su Ubuntu 14.04, GNU grep versione 2.16). Dov'è implementata la logica "usa il buffering di linea se stdout è una tty"? In git.savannah.gnu.org/cgit/grep.git/tree/src/grep.c , line_bufferedè impostato solo dall'argomento parser.
Aasmund Eldhuset,

8
@MichaelGoldshteyn Sono su macOS usando BSD grep e senza --line-bufferedottenere output. Tuttavia, dopo il test, sembra che GNU grep faccia quello che descrivi. Quindi, come la maggior parte delle cose Unix, dipende dall'implementazione della tua piattaforma. Poiché la domanda non specificava la piattaforma, le tue informazioni sembrano essere false - dopo aver esaminato il codice per BSD grep e averlo confrontato con GNU grep, il comportamento è sicuramente controllato dall'opzione --line-buffered. È solo che GNU grep scarica per impostazione predefinita.
Richard Waite,

119

Uso tail -f <file> | grep <pattern>tutto il tempo.

Aspetterà fino a quando grep arrossirà, non fino al termine (sto usando Ubuntu).


4
Che può durare un bel po ', quindi cerca di non diventare impaziente.
glglgl,

Quanto può durare all'incirca?
Matthieu Napoli,

@Matthieu: dipende principalmente da ciò che desideri e da quanto sono grandi i buffer sul tuo sistema operativo. Se grep corrisponde solo a una stringa corta ogni poche ore, saranno giorni prima del primo flush.
Tripleee,

13
Tail non usa il buffering dell'output - lo fa grep.
XzKto

7
No, grep non esegue il buffering dell'output quando l'output sta andando su un dispositivo tty, come è chiaramente in questa risposta. Fa buffering di linea! Questa è la risposta corretta e dovrebbe essere la risposta accettata. Vedi il mio commento più lungo alla risposta attualmente accettata ( sbagliata ) per maggiori dettagli.
Michael Goldshteyn,

67

Penso che il tuo problema sia che grep usa un po 'di buffering di output. Provare

tail -f file | stdbuf -o0 grep my_pattern

imposterà la modalità di buffering dell'output di grep su senza buffer.


7
E questo ha il vantaggio che può essere utilizzato anche per molti altri comandi grep.
Peter V. Mørch,

4
Tuttavia, come ho scoperto dopo aver giocato di più con esso, alcuni comandi scaricano il loro output solo quando sono collegati a un tty, e per questo, unbuffer(nel expect-devpacchetto su debian) è re . Quindi userei unbuffer su stdbuf.
Peter V. Mørch,

5
@Peter V. Mørch Sì, hai ragione, a volte il buffer può funzionare dove stdbuf non può. Ma penso che tu stia cercando di trovare un programma "magico" che risolva sempre i tuoi problemi invece di comprenderli. La creazione di un tty virtuale è un'attività non correlata. Stdbuf fa esattamente quello che vogliamo (imposta il buffer di output standard per dare valore), mentre unbuffer fa molte cose nascoste che potremmo non volere (confronta interattivo topcon stdbuf e unbuffer). E non esiste davvero una soluzione "magica": a volte anche il buffer non riesce, ad esempio awk utilizza un'implementazione del buffer diversa (anche stdbuf fallirà).
XzK al

2
"Ma penso che tu stia cercando di trovare un programma 'magico' che risolva sempre i tuoi problemi invece di comprenderli." - Penso tu abbia ragione! ;-)
Peter V. Mørch il

1
Qualche informazione in più su stdbuf, `unbuffer, e buffering stdio su pixelbeat.org/programming/stdio_buffering
Tor Klingberg

13

Se vuoi trovare corrispondenze nell'intero file (non solo nella coda) e vuoi che sieda e attenda nuove corrispondenze, funziona perfettamente:

tail -c +0 -f <file> | grep --line-buffered <pattern>

Il -c +0flag indica che l'output dovrebbe iniziare 0bytes ( -c) dall'inizio ( +) del file.


12

Nella maggior parte dei casi, puoi tail -f /var/log/some.log |grep fooe funzionerà perfettamente.

Se è necessario utilizzare più greps su un file di registro in esecuzione e non si ottiene alcun output, potrebbe essere necessario --line-bufferedinserire l' interruttore nei grep centrali , in questo modo:

tail -f /var/log/some.log | grep --line-buffered foo | grep bar

7

puoi considerare questa risposta come un miglioramento .. di solito sto usando

tail -F <fileName> | grep --line-buffered  <pattern> -A 3 -B 5

-F è meglio in caso di rotazione del file (-f non funzionerà correttamente se il file viene ruotato)

-A e -B sono utili per ottenere le linee appena prima e dopo il verificarsi del modello .. questi blocchi appariranno tra i separatori di linee tratteggiate

Ma per me preferisco fare quanto segue

tail -F <file> | less

questo è molto utile se si desidera cercare nei registri in streaming. Intendo andare avanti e indietro e guardare in profondità


4
grep -C 3 <pattern>, sostituisce -A <N> e -B <N> se N è lo stesso.
AKS

6

Non ho visto nessuno offrire il mio solito go-to per questo:

less +F <file>
ctrl + c
/<search term>
<enter>
shift + f

Preferisco questo, perché è possibile utilizzare ctrl + cper interrompere e navigare nel file ogni volta, quindi premere semplicemente shift + fper tornare alla ricerca live in streaming.


4

sed sarebbe una scelta migliore ( stream editor)

tail -n0 -f <file> | sed -n '/search string/p'

e poi se volevi che il comando tail uscisse una volta trovata una stringa particolare:

tail --pid=$(($BASHPID+1)) -n0 -f <file> | sed -n '/search string/{p; q}'

Ovviamente un bashism: $ BASHPID sarà l'ID di processo del comando tail. Il comando sed si trova dopo la coda nel pipe, quindi l'id del processo sed sarà $ BASHPID + 1.


1
Il presupposto che il prossimo processo avviato sul sistema ( $BASHPID+1) sarà tuo è falso in molte situazioni, e questo non fa nulla per risolvere il problema del buffering, che è probabilmente ciò su cui l'OP stava cercando di chiedere. In particolare, raccomandando sednel corso grepqui sembra solo una questione di preferenza (dubbia). (È possibile ottenere un p;qcomportamento grep -m 1se questo è il punto che si sta tentando di fornire.)
Tripleee

Funziona, il comando sed stampa ogni riga non appena ce ne sono pronti, il comando grep con --line-bufferedno. Sinceramente non capisco il meno 1.
MUY Belgio,

È stato finora stabilito che il buffering è il problema con grep . Non è richiesta alcuna azione speciale per gestire il buffering di linea usando sed , è il comportamento predefinito, quindi la mia enfasi sul flusso di parole . E vero, non vi è alcuna garanzia che $ BASHPID + 1 sarà il pid corretto da seguire, ma poiché l'allocazione del pid è sequenziale e al comando piped viene assegnato un pid immediatamente successivo, è assolutamente probabile.
Christian Herr,

1

Sì, questo funzionerà davvero bene. Grepe la maggior parte dei comandi Unix operano su flussi una riga alla volta. Ogni linea che esce dalla coda verrà analizzata e trasmessa se corrisponde.


2
In realtà non è corretto. Se grepè l'ultimo comando nella catena di pipe, agirà come spiegato. Tuttavia, se è nel mezzo, buffererà circa 8k output alla volta.
Mahmoud Al-Qudsi,

1

Questo comando funziona per me (Suse):

mail-srv:/var/log # tail -f /var/log/mail.info |grep --line-buffered LOGIN  >> logins_to_mail

raccolta accessi al servizio di posta


-1

di certo non ci riuscirai

tail -f /var/log/foo.log |grep --line-buffered string2search

quando usi "colortail" come alias per la coda, ad es. in bash

alias tail='colortail -n 30'

puoi controllare per tipo alias se questo produce qualcosa come tail alan di alias colortail -n 30. allora hai il tuo colpevole :)

Soluzione:

rimuovere l'alias con

unalias tail

assicurati di usare il binario di coda "reale" con questo comando

type tail

che dovrebbe produrre qualcosa di simile:

tail is /usr/bin/tail

e quindi puoi eseguire il tuo comando

tail -f foo.log |grep --line-buffered something

In bocca al lupo.


-4

Usa awk (un'altra grande utility bash) invece di grep dove non hai l'opzione buffer di linea! Trasmetterà continuamente i tuoi dati dalla coda.

questo è il modo in cui usi grep

tail -f <file> | grep pattern

Ecco come useresti awk

tail -f <file> | awk '/pattern/{print $0}'

6
Questo non è corretto; Awk out of the box esegue il buffering di linea, proprio come la maggior parte degli altri strumenti Unix standard. (Inoltre, {print $0}è ridondante, poiché la stampa è l'azione predefinita quando passa una condizione.)
tripleee
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.