Es gibt einige Möglichkeiten, um tailzum Ausgang zu gelangen :
Schlechter Ansatz: Erzwinge taildas Schreiben einer weiteren Zeile
Sie können taildas Schreiben einer weiteren Ausgabezeile erzwingen , sobald grepeine Übereinstimmung gefunden und beendet wurde. Dies führt dazu tail, dass ein SIGPIPEangezeigt wird und das Programm beendet wird. Eine Möglichkeit, dies zu tun, besteht darin, die Datei zu ändern, die tailnach dem grepBeenden überwacht wird .
Hier ist ein Beispielcode:
tail -f logfile.log | grep -m 1 "Server Started" | { cat; echo >>logfile.log; }
In diesem Beispiel catwird nicht beendet, bis grepdie Standardausgabe geschlossen wurde. Daher tailist es unwahrscheinlich, dass in die Pipe geschrieben werden kann, bevor grepdie Standardausgabe geschlossen werden konnte. catwird verwendet, um die Standardausgabe von grepunmodified zu verbreiten.
Dieser Ansatz ist relativ einfach, es gibt jedoch mehrere Nachteile:
- Wenn
grepstdout vor stdin geschlossen wird, gibt es immer eine Racebedingung: grepschließt stdout, löst aus cat, löst aus echo, löst aus tail, gibt eine Zeile aus. Wenn diese Zeile an gesendet wurde, grepbevor grepstdin geschlossen werden konnte, tailwird sie erst abgerufen, wenn eine SIGPIPEandere Zeile geschrieben wurde.
- Es erfordert Schreibzugriff auf die Protokolldatei.
- Sie müssen mit dem Ändern der Protokolldatei einverstanden sein.
- Sie können die Protokolldatei beschädigen, wenn Sie gleichzeitig mit einem anderen Prozess schreiben (die Schreibvorgänge werden möglicherweise verschachtelt, sodass eine neue Zeile in der Mitte einer Protokollmeldung angezeigt wird).
- Dieser Ansatz ist spezifisch für: Er
tailfunktioniert nicht mit anderen Programmen.
- Die dritte Pipeline-Stufe erschwert den Zugriff auf den Rückkehrcode der zweiten Pipeline-Stufe (es sei denn, Sie verwenden eine POSIX-Erweiterung wie
bashdas PIPESTATUSArray von). Dies ist in diesem Fall keine große Sache, da grepimmer 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 grepzurückkehrt. Zum Beispiel:
fifo=/tmp/tmpfifo.$$
mkfifo "${fifo}" || exit 1
tail -f logfile.log >${fifo} &
tailpid=$! # optional
grep -m 1 "Server Started" "${fifo}"
kill "${tailpid}" # optional
rm "${fifo}"
Die mit dem Kommentar gekennzeichneten Zeilen # optionalkönnen entfernt werden und das Programm funktioniert weiterhin. tailwird nur so lange verweilen, bis eine andere Eingabezeile gelesen oder durch einen anderen Prozess beendet wird.
Die Vorteile dieses Ansatzes sind:
- Sie müssen die Protokolldatei nicht ändern
- Der Ansatz funktioniert auch für andere Versorgungsunternehmen
tail
- es leidet nicht unter einer Rennbedingung
- Sie können leicht den Rückgabewert von
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 tailPipeline-Phase beenden, indem Sie ein Signal wie senden SIGTERM. Die Herausforderung besteht darin, zwei Dinge an derselben Stelle im Code zuverlässig zu kennen: taildie PID und ob grepsie beendet wurden.
Bei einer Pipeline wie dieser tail -f ... | grep ...ist es einfach, die erste Pipelinestufe zu ändern, um taildie PID in einer Variablen durch Hintergrundinformationen tailund Lesen zu speichern $!. Es ist auch einfach, die zweite Pipeline-Stufe so zu ändern, dass sie killbeim grepBeenden 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 die tailPID irgendwie herausfinden, damit sie tailbei grepRückkehr töten kann , oder die erste Stufe muss irgendwie benachrichtigt werden, wenn grepRückkehr erfolgt.
Die zweite Stufe könnte pgrepzum Abrufen tailder PID verwendet werden, dies wäre jedoch unzuverlässig (möglicherweise stimmen Sie mit dem falschen Prozess überein) und nicht portierbar ( pgrepwird vom POSIX-Standard nicht angegeben).
Die erste Stufe könnte die PID durch echoEingabe der PID über die Pipe an die zweite Stufe senden , aber diese Zeichenfolge wird mit tailder Ausgabe von gemischt . Das Demultiplexen der beiden kann abhängig von der Ausgabe von ein komplexes Escape-Schema erfordern tail.
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öten tail. Hier ist ein Beispielcode:
fifo=/tmp/notifyfifo.$$
mkfifo "${fifo}" || exit 1
{
# run tail in the background so that the shell can
# kill tail when notified that grep has exited
tail -f logfile.log &
# remember tail's PID
tailpid=$!
# wait for notification that grep has exited
read foo <${fifo}
# grep has exited, time to go
kill "${tailpid}"
} | {
grep -m 1 "Server Started"
# notify the first pipeline stage that grep is done
echo >${fifo}
}
# clean up
rm "${fifo}"
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 taildie Ausgabe möglicherweise grepfür eine willkürlich lange Zeit nicht verarbeitet wird . Auf GNU-Systemen sollte es keine Probleme geben: GNU grepverwendet read(), wodurch jegliches Puffern vermieden wird, und GNU tail -fruft 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.