Ich verwende tail -f, um eine Protokolldatei zu überwachen, in die aktiv geschrieben wird. Wenn eine bestimmte Zeichenfolge in die Protokolldatei geschrieben wird, möchte ich die Überwachung beenden und mit dem Rest meines Skripts fortfahren.
Zur Zeit benutze ich:
tail -f logfile.log | grep -m 1 "Server Started"
Wenn die Zeichenfolge gefunden wird, wird grep wie erwartet beendet, aber ich muss eine Möglichkeit finden, den Befehl tail ebenfalls zu beenden, damit das Skript fortgesetzt werden kann.
tail
stirbt erst in der nächsten Zeile. Versuchen Sie Folgendes:date > log; tail -f log | grep -m 1 trigger
und dann in einer anderen Shell:echo trigger >> log
und Sie sehen die Ausgabetrigger
in der ersten Shell, aber keine Beendigung des Befehls. Dann versuchen Sie:date >> log
in der zweiten Shell und der Befehl in der ersten Shell wird beendet. Aber manchmal ist das zu spät; Wir möchten beenden, sobald die Trigger-Zeile angezeigt wird, nicht, wenn die Zeile nach der Trigger-Zeile vollständig ist.tail
+grep -q
wie die Antwort von 00prometheus zu verwendenAntworten:
Ein einfacher POSIX-Einzeiler
Hier ist ein einfacher Einzeiler. Es sind keine bashspezifischen oder nicht-POSIX-Tricks oder sogar eine Named Pipe erforderlich. Alles , was Sie wirklich brauchen , ist die Kündigung zu entkoppeln
tail
vongrep
. Auf diese Weise kanngrep
das Skript auch dann fortgesetzt werden, wenn estail
noch nicht beendet wurde. Mit dieser einfachen Methode gelangen Sie dorthin:grep
wird blockieren, bis es den String gefunden hat, woraufhin es beendet wird. Durchtail
Ausführen von seiner eigenen Sub-Shell aus können wir es in den Hintergrund stellen, sodass es unabhängig ausgeführt wird. In der Zwischenzeit kann die Haupt-Shell das Skript ausführen, sobald sie beendet istgrep
.tail
verbleibt in seiner Sub-Shell, bis die nächste Zeile in die Protokolldatei geschrieben wurde, und wird dann beendet (möglicherweise auch, nachdem das Hauptskript beendet wurde). Der Hauptpunkt ist, dass die Pipeline nicht mehr auftail
das Beenden wartet , sodass die Pipeline sofort nach dem Beenden beendet wirdgrep
.Einige kleine Verbesserungen:
tail
das Lesen ab der aktuellen letzten Zeile der Protokolldatei gestartet, falls die Zeichenfolge früher in der Protokolldatei vorhanden ist.tail
-F anstelle von -f angeben. Es ist kein POSIX, aber es ermöglichttail
das Arbeiten, auch wenn das Protokoll während des Wartens gedreht wird.grep
beendet das Programm nach dem ersten Auftreten, ohne jedoch die Triggerzeile auszudrucken . Es ist auch POSIX, was -m1 nicht ist.quelle
tail
für immer im Hintergrund laufen. Wie würden Sie dietail
PID in der Hintergrund-Sub-Shell erfassen und sie in der Haupt-Shell anzeigen? Ich kann die suboptionale Problemumgehung nur finden, indem ich alle mit der Sitzung verbundenentail
Prozesse mit beendepkill -s 0 tail
.tail
wird beendet, sobald versucht wird, in eine defekte Pipe zu schreiben. Die Pipe bricht ab, sobald dergrep
Vorgang abgeschlossen ist. Sobald dergrep
Vorgang abgeschlossen ist,tail
wird die Pipe beendet, sobald die Protokolldatei eine weitere Zeile enthält.tail -f
.Die akzeptierte Antwort funktioniert bei mir nicht. Außerdem ist sie verwirrend und ändert die Protokolldatei.
Ich benutze so etwas:
Wenn die Protokollzeile mit dem Muster übereinstimmt, beenden Sie das
tail
von diesem Skript gestartete.Hinweis: Wenn Sie die Ausgabe auch auf dem Bildschirm anzeigen möchten, können Sie entweder
| tee /dev/tty
die Zeile vor dem Testen in der while-Schleife wiederholen.quelle
pkill
nicht von POSIX angegeben und ist nicht überall verfügbar.Wenn Sie Bash verwenden (zumindest, aber es scheint, dass es nicht durch POSIX definiert ist, so dass es in einigen Shells fehlen kann), können Sie die Syntax verwenden
Es funktioniert ziemlich genau wie die bereits erwähnten FIFO-Lösungen, ist jedoch viel einfacher zu schreiben.
quelle
SIGTERM
(Strg + C, Befehltail
liest es, versucht es auszugeben und erhält dann ein SIGPIPE, das es beendet. Im Prinzip haben Sie also Recht. Dastail
kann auf unbestimmte Zeit ausgeführt werden, wenn nie wieder etwas in die Protokolldatei geschrieben wird. In der Praxis könnte dies für viele Menschen eine sehr saubere Lösung sein.Es gibt einige Möglichkeiten, um
tail
zum Ausgang zu gelangen :Schlechter Ansatz: Erzwinge
tail
das Schreiben einer weiteren ZeileSie können
tail
das Schreiben einer weiteren Ausgabezeile erzwingen , sobaldgrep
eine Übereinstimmung gefunden und beendet wurde. Dies führt dazutail
, dass einSIGPIPE
angezeigt wird und das Programm beendet wird. Eine Möglichkeit, dies zu tun, besteht darin, die Datei zu ändern, dietail
nach demgrep
Beenden überwacht wird .Hier ist ein Beispielcode:
In diesem Beispiel
cat
wird nicht beendet, bisgrep
die Standardausgabe geschlossen wurde. Dahertail
ist es unwahrscheinlich, dass in die Pipe geschrieben werden kann, bevorgrep
die Standardausgabe geschlossen werden konnte.cat
wird verwendet, um die Standardausgabe vongrep
unmodified zu verbreiten.Dieser Ansatz ist relativ einfach, es gibt jedoch mehrere Nachteile:
grep
stdout vor stdin geschlossen wird, gibt es immer eine Racebedingung:grep
schließt stdout, löst auscat
, löst ausecho
, löst austail
, gibt eine Zeile aus. Wenn diese Zeile an gesendet wurde,grep
bevorgrep
stdin geschlossen werden konnte,tail
wird sie erst abgerufen, wenn eineSIGPIPE
andere Zeile geschrieben wurde.tail
funktioniert nicht mit anderen Programmen.bash
dasPIPESTATUS
Array von). Dies ist in diesem Fall keine große Sache, dagrep
immer 0 zurückgegeben wird. Im Allgemeinen wird die mittlere Stufe jedoch durch einen anderen Befehl ersetzt, dessen Rückkehrcode Sie interessieren (z. B. etwas, das 0 zurückgibt, wenn "Server gestartet" erkannt wird, 1 wenn "Server konnte nicht gestartet werden" erkannt wird).Die nächsten Ansätze umgehen diese Einschränkungen.
Ein besserer Ansatz: Vermeiden Sie Pipelines
Sie können ein FIFO verwenden, um die Pipeline vollständig zu umgehen und die Ausführung fortzusetzen, sobald sie
grep
zurückkehrt. Zum Beispiel:Die mit dem Kommentar gekennzeichneten Zeilen
# optional
können entfernt werden und das Programm funktioniert weiterhin.tail
wird nur so lange verweilen, bis eine andere Eingabezeile gelesen oder durch einen anderen Prozess beendet wird.Die Vorteile dieses Ansatzes sind:
tail
grep
(oder den von Ihnen verwendeten alternativen Befehl) abrufen.Der Nachteil dieses Ansatzes ist die Komplexität, insbesondere die Verwaltung des FIFO: Sie müssen sicher einen temporären Dateinamen generieren und sicherstellen, dass das temporäre FIFO gelöscht wird, auch wenn der Benutzer in der Mitte von Strg-C drückt das Drehbuch. Dies kann mit einer Falle geschehen.
Alternativer Ansatz: Senden Sie eine Nachricht an Kill
tail
Sie können die
tail
Pipeline-Phase beenden, indem Sie ein Signal wie sendenSIGTERM
. Die Herausforderung besteht darin, zwei Dinge an derselben Stelle im Code zuverlässig zu kennen:tail
die PID und obgrep
sie beendet wurden.Bei einer Pipeline wie dieser
tail -f ... | grep ...
ist es einfach, die erste Pipelinestufe zu ändern, umtail
die PID in einer Variablen durch Hintergrundinformationentail
und Lesen zu speichern$!
. Es ist auch einfach, die zweite Pipeline-Stufe so zu ändern, dass siekill
beimgrep
Beenden ausgeführt wird. Das Problem ist, dass die beiden Phasen der Pipeline in separaten "Ausführungsumgebungen" (in der Terminologie des POSIX-Standards) ausgeführt werden, sodass die zweite Pipeline-Phase keine Variablen lesen kann, die von der ersten Pipeline-Phase festgelegt wurden. Ohne die Verwendung von Shell-Variablen muss entweder die zweite Stufe dietail
PID irgendwie herausfinden, damit sietail
beigrep
Rückkehr töten kann , oder die erste Stufe muss irgendwie benachrichtigt werden, wenngrep
Rückkehr erfolgt.Die zweite Stufe könnte
pgrep
zum Abrufentail
der PID verwendet werden, dies wäre jedoch unzuverlässig (möglicherweise stimmen Sie mit dem falschen Prozess überein) und nicht portierbar (pgrep
wird vom POSIX-Standard nicht angegeben).Die erste Stufe könnte die PID durch
echo
Eingabe der PID über die Pipe an die zweite Stufe senden , aber diese Zeichenfolge wird mittail
der Ausgabe von gemischt . Das Demultiplexen der beiden kann abhängig von der Ausgabe von ein komplexes Escape-Schema erforderntail
.Sie können ein FIFO verwenden, damit die zweite Pipeline-Stufe die erste Pipeline-Stufe benachrichtigt, wenn sie beendet wird
grep
. Dann kann die erste Stufe tötentail
. Hier ist ein Beispielcode:Dieser Ansatz hat alle Vor- und Nachteile des vorherigen Ansatzes, mit der Ausnahme, dass er komplizierter ist.
Eine Warnung zum Puffern
Mit POSIX können die Streams stdin und stdout vollständig gepuffert werden, was bedeutet, dass
tail
die Ausgabe möglicherweisegrep
für eine willkürlich lange Zeit nicht verarbeitet wird . Auf GNU-Systemen sollte es keine Probleme geben: GNUgrep
verwendetread()
, wodurch jegliches Puffern vermieden wird, und GNUtail -f
ruft regelmäßig auf,fflush()
wenn auf stdout geschrieben wird. Nicht-GNU-Systeme müssen möglicherweise etwas Besonderes tun, um Puffer zu deaktivieren oder regelmäßig zu leeren.quelle
tail -f
gibt nur die letzten zehn Zeilen und dann alle folgenden aus. Um dies zu verbessern, können Sie die Option-n 10000
zum Ende hinzufügen , sodass auch die letzten 10000 Zeilen ausgegeben werden.tail -f
durch die Fifo und greppen darauf:mkfifo f; tail -f log > f & tailpid=$! ; grep -m 1 trigger f; kill $tailpid; rm f
.tail -f log
in ein FIFO schreibe, werden einige Systeme (z. B. GNU / Linux) blockbasiertes Puffern anstelle von zeilenbasiertem Puffern verwenden, was bedeutet,grep
dass die übereinstimmende Zeile möglicherweise nicht angezeigt wird wird im Protokoll angezeigt. Das System bietet möglicherweise ein Hilfsprogramm zum Ändern der Pufferung, z. B.stdbuf
von GNU coreutils. Ein solches Dienstprogramm wäre jedoch nicht portabel.grep -q -m 1 trigger <(tail -f log)
anderswo vor und lebe damit, dass dastail
eine Zeile länger im Hintergrund läuft als es muss.Lassen Sie mich auf @ 00prometheus Antwort (die die beste ist) erweitern.
Vielleicht sollten Sie eine Zeitüberschreitung verwenden, anstatt auf unbestimmte Zeit zu warten.
Die unten stehende Bash-Funktion wird blockiert, bis der angegebene Suchbegriff angezeigt wird oder eine bestimmte Zeitüberschreitung erreicht ist.
Der Beendigungsstatus ist 0, wenn die Zeichenfolge innerhalb des Zeitlimits gefunden wird.
Möglicherweise ist die Protokolldatei noch nicht vorhanden, nachdem Sie Ihren Server gestartet haben. In diesem Fall sollten Sie warten, bis es angezeigt wird, bevor Sie nach der Zeichenfolge suchen:
So können Sie es verwenden:
quelle
timeout
Kommando?timeout
die einzige zuverlässige Methode, um nicht auf unbestimmte Zeit auf einen Server zu warten, der nicht gestartet werden kann und bereits beendet wurde.Nachdem ich einige Tests durchgeführt hatte, fand ich einen schnellen Weg, um diese Arbeit zu machen. Es scheint, dass tail -f beendet wird, wenn grep beendet wird, aber es gibt einen Haken. Es scheint nur ausgelöst zu werden, wenn die Datei geöffnet und geschlossen wird. Ich habe dies erreicht, indem ich die leere Zeichenfolge an die Datei angefügt habe, wenn grep die Übereinstimmung findet.
Ich bin mir nicht sicher, warum das Öffnen / Schließen der Datei den Endpunkt auslöst, um zu erkennen, dass die Pipe geschlossen ist, sodass ich mich nicht auf dieses Verhalten verlassen würde. aber es scheint vorerst zu funktionieren.
Grund dafür ist, dass es schließt, schauen Sie sich das -F-Flag im Vergleich zum -f-Flag an.
quelle
tail
, dass eine andere Zeile ausgegeben wird, bis dahin jedoch beendetgrep
wurde (wahrscheinlich - es liegt eine Race-Bedingung vor). Wenngrep
bis zum Ende der Zeit einetail
andere Zeile geschrieben wurde,tail
wird einSIGPIPE
. Das führttail
dazu, dass man sofort abreist.tail
(6) Sie können es nicht einfach so einstellen, dass es sich je nach String-Übereinstimmungen unterschiedlich verhält ("server started" vs. "server start failed"), da Sie den Rückkehrcode nicht einfach erhalten können der mittleren Stufe der Pipeline. Es gibt einen alternativen Ansatz, der all diese Probleme vermeidet - siehe meine Antwort.Derzeit
tail -f
laufen alle hier beschriebenen Lösungen Gefahr, eine zuvor protokollierte "Server Started" -Zeile zu übernehmen (was in Ihrem speziellen Fall je nach Anzahl der protokollierten Zeilen und der Protokolldateirotation ein Problem sein kann oder nicht). Kürzung).Anstatt Dinge zu komplizieren, benutze einfach ein schlaueres
tail
, wie bmike mit einem Perl-Snippit zeigte. Die einfachste Lösung ist dieseretail
mit integrierter Regex-Unterstützung mit Start- und Stopp- Bedingungsmustern:Dies folgt der Datei wie eine normale Datei,
tail -f
bis die erste neue Instanz dieser Zeichenfolge angezeigt wird. Beenden Sie dann die Datei . (Die-u
Option wird im normalen "Follow" -Modus nicht für vorhandene Zeilen in den letzten 10 Zeilen der Datei ausgelöst.)Wenn Sie GNU
tail
(von coreutils ) verwenden, ist die nächst einfachere Option--pid
und ein FIFO (Named Pipe):Ein FIFO wird verwendet, da die Prozesse separat gestartet werden müssen, um eine PID zu erhalten und zu übergeben. Ein FIFO leidet immer noch unter dem gleichen Problem , für eine rechtzeitige Schreiben auf Grund rumhängen
tail
erhielt eine SIGPIPE , verwenden Sie die--pid
Option , so dasstail
Ausfahrten , wenn es merkt , dassgrep
beendet hat (üblicherweise verwendet , um die zu überwachen Schreiber Prozess und nicht als der Leser , abertail
doesn‘ ist mir wirklich egal). Option-n 0
wird mit verwendet,tail
damit alte Zeilen keine Übereinstimmung auslösen.Sie können auch einen Stateful-Tail verwenden , der den aktuellen Datei-Offset speichert, sodass nachfolgende Aufrufe nur neue Zeilen anzeigen (auch die Dateirotation wird behandelt). In diesem Beispiel wird das alte FWTK
retail
* verwendet:* Hinweis, gleicher Name, anderes Programm als bei der vorherigen Option.
Vergleichen Sie den Zeitstempel der Datei mit der Statusdatei (
.${LOGFILE}.off
), anstatt eine CPU-Überlastungsschleife zu haben , und warten Sie. Verwenden Sie "-T
", um den Speicherort der Statusdatei anzugeben, falls erforderlich. Oben wird das aktuelle Verzeichnis vorausgesetzt. Sie können diese Bedingung jederzeit überspringen oder unter Linux die effizientereinotifywait
verwenden:quelle
retail
mit einer Zeitüberschreitung kombinieren, z. B .: "Wenn 120 Sekunden vergangen sind und der Einzelhandel die Zeile noch nicht gelesen hat, geben Sie einen Fehlercode ein und beenden Sie den Einzelhandel"?timeout
(coreutils) zur Einführungretail
und prüfen nur für Exit - Code 124 auf timeout (timeout
töten , was auch immer befehlen Sie es verwenden , nach der Zeit starten Sie setzen)Dies ist etwas knifflig, da Sie sich mit der Prozesssteuerung und -signalisierung befassen müssen. Kluger wäre eine Zwei-Skript-Lösung mit PID-Tracking. Besser wäre es, Named Pipes wie diese zu verwenden.
Welches Shell-Skript verwenden Sie?
Für eine schnelle und schmutzige Ein-Skript-Lösung würde ich ein Perl-Skript mit File: Tail erstellen
Anstatt in der while-Schleife zu drucken, können Sie nach der Zeichenfolgenübereinstimmung filtern und aus der while-Schleife ausbrechen, damit Ihr Skript fortgesetzt wird.
In beiden Fällen sollten Sie nur ein wenig lernen, um die gewünschte Überwachungsflusssteuerung zu implementieren.
quelle
maxinterval=>300
bedeutet, dass die Datei alle fünf Minuten überprüft wird. Da ich weiß, dass meine Zeile vorübergehend in der Datei angezeigt wird, verwende ich eine viel aggressivere Abfrage:maxinterval=>0.2, adjustafter=>10000
Warten Sie, bis die Datei angezeigt wird
Warten Sie, bis der String in der Datei angezeigt wird
https://superuser.com/a/743693/129669
quelle
/path/to/the.file
die 1,4 GB groß ist; dann ist klar, dass dies ein problem ist. 2. Es wartet länger als nötig, wenn der Protokolleintrag angezeigt wurde, im schlimmsten Fall 10s.Ich kann mir keine sauberere Lösung vorstellen als diese:
ok, vielleicht kann der Name noch verbessert werden ...
Vorteile:
quelle
Dafür brauchst du keinen Schwanz. Ich denke, der Befehl watch ist das, wonach Sie suchen. Der Befehl watch überwacht die Ausgabe einer Datei und kann mit der Option -g beendet werden, wenn sich die Ausgabe ändert.
quelle
Alex, ich denke, dieser wird dir sehr helfen.
Dieser Befehl gibt niemals einen Eintrag in die Logdatei, sondern greift stumm ...
quelle
logfile
da es ansonsten eine willkürlich lange Zeit dauern kanntail
, bis eine andere Zeile ausgegebengrep
wird und erkannt wird, dass (viaSIGPIPE
) gestorben ist .Hier ist eine viel bessere Lösung, bei der Sie nicht in die Protokolldatei schreiben müssen, was in einigen Fällen sehr gefährlich oder sogar unmöglich ist.
Derzeit hat es nur eine Nebenwirkung, der
tail
Prozess bleibt im Hintergrund, bis die nächste Zeile in das Protokoll geschrieben wird.quelle
tail -n +0 -f
beginnt am Anfang der Datei.tail -n 0 -f
beginnt am Ende der Datei.myscript.sh: line 14: 7845 Terminated sh -c 'tail...
tail
Prozess wird weiterhin im Hintergrund ausgeführt.Die anderen Lösungen hier haben mehrere Probleme:
Folgendes habe ich am Beispiel von Tomcat erfunden (entfernen Sie die Hashes, wenn Sie das Protokoll beim Starten anzeigen möchten):
quelle
Der
tail
Befehl kann im Hintergrund ausgeführt werden und seinegrep
PID wird in der Subshell wiedergegeben. In dergrep
Subshell kann ein Trap-Handler bei EXIT dentail
Befehl beenden .quelle
Lies sie alle. tldr: entkopple die Beendigung von tail von grep.
Die zwei bequemsten Formen sind
und wenn du bash hast
Aber wenn dich dieser Schwanz im Hintergrund stört, gibt es hier einen schöneren Weg als eine Fifo oder eine andere Antwort. Benötigt Bash.
Oder wenn es nicht der Schwanz ist, der Dinge ausgibt,
quelle
Versuche inotify zu benutzen (inotifywait)
Sie richten inotifywait für jede Dateiänderung ein und überprüfen dann die Datei mit grep. Wenn sie nicht gefunden wird, führen Sie inotifywait erneut aus, wenn sie gefunden wird, beenden Sie die Schleife ... Smth like that
quelle
$!
; inotifywait -e MODIFY / tmp / found; kill -KILL - $ MYPIDSie möchten gehen, sobald die Zeile geschrieben ist, aber Sie möchten auch nach einer Zeitüberschreitung gehen:
quelle
Wie wäre es damit:
während wahr; machen Sie als ob [ ! -z $ (grep "myRegEx" myLog.log)]; dann brechen; fi; getan
quelle