Wie kann man einen kontinuierlichen Stream "grep"?

729

Ist das in grepeinem kontinuierlichen Stream möglich?

Was ich meine, ist eine Art tail -f <file>Befehl, aber mit grepauf der Ausgabe, um nur die Zeilen zu behalten, die mich interessieren.

Ich habe es versucht, tail -f <file> | grep patternaber es scheint, dass grepes nur ausgeführt werden kann, wenn es tailfertig ist, das heißt nie.

Matthieu Napoli
quelle
9
Es ist sehr wahrscheinlich, dass das Programm, das die Datei generiert, seine Ausgabe nicht löscht.
Steve-o
tail -f filefunktioniert (ich sehe die neue Ausgabe in Echtzeit)
Matthieu Napoli
6
Wäre angemessen für unix.stackexchange.com
Luc M
@ Luc in der Tat nicht daran gedacht
Matthieu Napoli
Möglicherweise enthält Ihr Eingabestream keine neuen Zeilen? Wenn ja, wird grep nicht fortfahren.
Lynch

Antworten:

1327

Aktivieren Sie den grepZeilenpuffermodus, wenn Sie BSD grep verwenden (FreeBSD, Mac OS X usw.).

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

Sie müssen dies nicht für GNU grep tun (wird unter so ziemlich jedem Linux verwendet), da es standardmäßig geleert wird (YMMV für andere Unix-Likes wie SmartOS, AIX oder QNX).

bisschen
quelle
3
@MichaelNiemand Sie könnten tail -F Datei | verwenden grep --line-buffered my_pattern
jcfrei
47
@ MichaelGoldshteyn Mach es ruhig. Die Leute stimmen dem zu, weil sie diese Seite finden, wenn sie "grep line buffered" googeln, und es löst ein Problem für sie, das möglicherweise nicht genau das ist, was als Frage gestellt wird.
Raine
4
Ich bin hierher gekommen, um die Ausgabe von zu erfassen strace. Ohne das --line-bufferedwird es nicht funktionieren.
Sjas
5
@MichaelGoldshteyn (und die Aufsteiger seines Kommentars): Ich hatte dieses Problem immer mit tail -f | grepund --line-bufferedlöse es für mich (unter Ubuntu 14.04, GNU grep Version 2.16). Wo ist die Logik "Zeilenpufferung verwenden, wenn stdout eine tty ist" implementiert? In git.savannah.gnu.org/cgit/grep.git/tree/src/grep.c , line_bufferedwird nur durch das Argument Parser.
Aasmund Eldhuset
8
@MichaelGoldshteyn Ich bin auf MacOS mit BSD Grep und ohne --line-bufferedbekomme ich keine Ausgabe. Nach dem Testen sieht es jedoch so aus, als ob GNU grep das tut, was Sie beschreiben. Wie bei den meisten Unix-Dingen hängt dies von der Implementierung Ihrer Plattform ab. Da in der Frage keine Plattform angegeben wurde, scheinen Ihre Informationen falsch zu sein. Nachdem Sie den Code für BSD grep überprüft und mit GNU grep verglichen haben, wird das Verhalten definitiv durch die Option --line-buffered gesteuert. Es ist nur so, dass standardmäßig nur GNU grep gelöscht wird.
Richard Waite
118

Ich benutze das die tail -f <file> | grep <pattern>ganze Zeit.

Es wird warten, bis grep errötet, nicht bis es beendet ist (ich benutze Ubuntu).

Irit Katriel
quelle
4
Das kann eine ganze Weile dauern, also versuche nicht ungeduldig zu werden.
glglgl
Wie lange kann es ungefähr dauern?
Matthieu Napoli
@Matthieu: Hängt hauptsächlich davon ab, wonach Sie suchen und wie groß die Puffer auf Ihrem Betriebssystem sind. Wenn der Grep nur alle paar Stunden mit einer kurzen Zeichenfolge übereinstimmt, dauert es Tage vor dem ersten Flush.
Tripleee
13
Tail verwendet keine Ausgabepufferung - grep tut dies.
XzKto
7
Nein, grep führt keine Ausgabepufferung durch, wenn die Ausgabe an ein tty-Gerät gesendet wird, wie dies in dieser Antwort eindeutig angegeben ist. Es macht Zeilenpufferung! Dies ist die richtige Antwort und sollte die akzeptierte Antwort sein. Weitere Informationen finden Sie in meinem längeren Kommentar zur aktuell akzeptierten ( falschen ) Antwort.
Michael Goldshteyn
67

Ich denke, dass Ihr Problem darin besteht, dass grep eine gewisse Ausgabepufferung verwendet. Versuchen

tail -f file | stdbuf -o0 grep my_pattern

Dadurch wird der Ausgabepuffermodus von grep auf ungepuffert gesetzt.

XzKto
quelle
7
Und das hat den Vorteil, dass es auch für viele andere Befehle verwendet werden kann grep.
Peter V. Mørch
4
Wie ich jedoch festgestellt habe, nachdem ich mehr damit gespielt habe, leeren einige Befehle ihre Ausgabe nur, wenn sie mit einem tty verbunden sind, und dafür unbuffer(im expect-devPaket auf debian) ist König . Also würde ich Unpuffer über stdbuf verwenden.
Peter V. Mørch
5
@Peter V. Mørch Ja, Sie haben Recht, Unpuffer kann manchmal funktionieren, wo stdbuf nicht kann. Aber ich denke, Sie versuchen, ein "magisches" Programm zu finden, das Ihre Probleme immer behebt, anstatt Ihr Problem zu verstehen. Das Erstellen eines virtuellen tty ist eine nicht verwandte Aufgabe. Stdbuf macht genau das, was wir wollen (setzt den Standardausgabepuffer auf Wert), während Unpuffer viele versteckte Dinge macht, die wir vielleicht nicht wollen (vergleiche interaktiv topmit Stdbuf und Unbuffer). Und es gibt wirklich keine "magische" Lösung: Unpuffer schlägt manchmal auch fehl, zum Beispiel verwendet awk eine andere Pufferimplementierung (stdbuf schlägt auch fehl).
XzKto
2
"Aber ich denke, Sie versuchen, ein 'magisches' Programm zu finden, das Ihre Probleme immer behebt, anstatt Ihr Problem zu verstehen." - Ich denke, du hast recht! ;-)
Peter V. Mørch
1
Weitere Informationen zu stdbuf`Unbuffer und Stdio Buffering unter pixelbeat.org/programming/stdio_buffering
Tor Klingberg
13

Wenn Sie Übereinstimmungen in der gesamten Datei finden möchten (nicht nur im Endbereich) und möchten, dass diese übereinstimmen und auf neue Übereinstimmungen warten, funktioniert dies einwandfrei:

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

Das -c +0Flag gibt an, dass die Ausgabe 0bytes ( -c) am Anfang ( +) der Datei beginnen soll.

Ken Williams
quelle
12

In den meisten Fällen können Sie tail -f /var/log/some.log |grep foound es wird gut funktionieren.

Wenn Sie mehrere Greps für eine laufende Protokolldatei verwenden müssen und feststellen, dass Sie keine Ausgabe erhalten, müssen Sie den --line-bufferedSchalter möglicherweise wie folgt in Ihre mittleren Greps stecken :

tail -f /var/log/some.log | grep --line-buffered foo | grep bar
Dale Anderson
quelle
7

Sie können diese Antwort als Verbesserung betrachten. Normalerweise verwende ich

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

-F ist besser, wenn die Datei gedreht wird (-f funktioniert nicht richtig, wenn die Datei gedreht wird)

-A und -B sind nützlich, um Linien unmittelbar vor und nach dem Auftreten des Musters abzurufen. Diese Blöcke werden zwischen gestrichelten Trennzeichen angezeigt

Aber für mich mache ich lieber Folgendes

tail -F <file> | less

Dies ist sehr nützlich, wenn Sie in gestreamten Protokollen suchen möchten. Ich meine, geh hin und her und schau tief

Mebada
quelle
4
grep -C 3 <pattern>, ersetzt -A <N> und -B <N>, wenn N gleich ist.
AKS
6

Ich habe nicht gesehen, dass jemand meine übliche Anlaufstelle dafür angeboten hat:

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

Ich bevorzuge dies, da Sie jederzeit ctrl + canhalten und durch die Datei navigieren und dann einfach drücken können shift + f, um zur Live-Streaming-Suche zurückzukehren.

Hans.Loven.work
quelle
4

sed wäre eine bessere Wahl ( Stream- Editor)

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

und wenn Sie möchten, dass der Befehl tail beendet wird, sobald Sie eine bestimmte Zeichenfolge gefunden haben:

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

Offensichtlich ein Bashismus: $ BASHPID ist die Prozess-ID des Befehls tail. Der Befehl sed steht nach dem Ende in der Pipe, sodass die sed-Prozess-ID $ BASHPID + 1 lautet.

Christian Herr
quelle
1
Die Annahme, dass der nächste auf dem System ( $BASHPID+1) gestartete Prozess Ihnen gehört, ist in vielen Situationen falsch, und dies trägt nicht zur Lösung des Pufferproblems bei, nach dem das OP wahrscheinlich zu fragen versuchte. Insbesondere empfehlen sedüber grepscheint , wie hier nur eine Frage der (fragwürdigen) bevorzugt. (Sie können p;qVerhalten mit bekommen, grep -m 1wenn das der Punkt ist, den Sie versuchen zu liefern.)
Tripleee
Funktioniert, druckt der Befehl sed jede Zeile, sobald sie fertig ist, der Befehl grep mit --line-bufferednicht. Ich verstehe das Minus 1 aufrichtig nicht.
MUY Belgien
Bisher wurde festgestellt, dass die Pufferung das Problem bei grep ist . Es ist keine spezielle Aktion erforderlich, um die Zeilenpufferung mit sed zu handhaben. Dies ist das Standardverhalten, daher liegt mein Schwerpunkt auf dem Wortstrom . Und wahr, es gibt keine Garantie dafür, dass $ BASHPID + 1 die richtige PID ist , aber da die PID-Zuweisung sequentiell ist und dem Piped-Befehl unmittelbar danach eine PID zugewiesen wird , ist dies äußerst wahrscheinlich.
Christian Herr
1

Ja, das wird tatsächlich gut funktionieren. GrepDie meisten Unix-Befehle werden zeilenweise für Streams ausgeführt. Jede Linie, die aus dem Schwanz kommt, wird analysiert und weitergeleitet, wenn sie übereinstimmt.

Caleb
quelle
2
Das ist eigentlich nicht richtig. Wenn dies grepder letzte Befehl in der Rohrkette ist, verhält er sich wie von Ihnen erklärt. Wenn es sich jedoch in der Mitte befindet, werden jeweils ca. 8.000 Ausgaben gepuffert.
Mahmoud Al-Qudsi
1

Dieser eine Befehl funktioniert für mich (Suse):

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

Sammeln von Anmeldungen beim Mail-Service

user10584393
quelle
-1

Sie werden sicherlich nicht erfolgreich sein

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

wenn Sie "colortail" als Alias ​​für tail verwenden, z. in bash

alias tail='colortail -n 30'

Sie können anhand des Typalias überprüfen, ob dies so etwas wie tail isan alias von ausgibt colortail -n 30. dann hast du deinen schuldigen :)

Lösung:

Entfernen Sie den Alias ​​mit

unalias tail

Stellen Sie sicher, dass Sie mit diesem Befehl die 'echte' Tail-Binärdatei verwenden

type tail

welches sollte etwas ausgeben wie:

tail is /usr/bin/tail

und dann können Sie Ihren Befehl ausführen

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

Viel Glück.

user882786
quelle
-4

Verwenden Sie awk (ein weiteres großartiges Bash-Dienstprogramm) anstelle von grep, wenn Sie die Option "Zeilenpuffer" nicht haben! Es wird kontinuierlich Ihre Daten vom Schwanz streamen.

So verwenden Sie grep

tail -f <file> | grep pattern

So würden Sie awk verwenden

tail -f <file> | awk '/pattern/{print $0}'
Atif
quelle
6
Das ist nicht richtig; Awk out of the box führt wie die meisten anderen Standard-Unix-Tools eine Zeilenpufferung durch. (Darüber hinaus ist das {print $0}redundant, da das Drucken die Standardaktion ist, wenn eine Bedingung erfüllt ist.)
Tripleee