Warum bedeutet „Schwanz -f… | Tail “keine Ausgabe produzieren?

36

Warum erzeugt der folgende Befehl keine Ausgabe?

$ tail -f /etc/passwd | tail

Nachdem ich über das Puffern gelesen hatte , versuchte ich Folgendes ohne Erfolg:

$ tail -f /etc/passwd | stdbuf -oL tail

Beachten Sie, dass Folgendes zu einer Ausgabe führt:

$ tail /etc/passwd | tail

So funktioniert das:

$ tail -f /etc/passwd | head

Ich benutze Tail Version 8.21 (GNU Coreutils).

thomie
quelle
17
Was sind die letzten 10 Stellen von π?
Keith Thompson

Antworten:

15

Ich dachte, ich hätte alles in UNIX gesehen. Diese Frage hat mich aus meiner Selbstgefälligkeit gerissen. Was für eine tolle Frage!

tailzeigt die letzten X Zeilen. tail -ffunktioniert genauso, aber im Wesentlichen in einer Endlosschleife: Zeigen Sie beim Start die letzten X Zeilen der Datei an, und verwenden Sie dann etwas Magie des Betriebssystems (wie inotify), überwachen Sie und zeigen Sie neue Zeilen an.

Um seine Arbeit zu erledigen, tailmuss es möglich sein, das Ende der Datei zu lokalisieren. Wenn taildas Dateiende nicht gefunden wird, werden die letzten X-Zeilen nicht angezeigt, da "last" undefiniert ist. Was macht tailman in diesem Fall? Es wartet, bis es das Ende der Datei findet.

Bedenken Sie:

$ chatter() { while :; do date; sleep 1; done; }
$ chatter | tail -f

Dies scheint niemals Fortschritte zu machen, da es nie ein definitives Dateiende gibt chatter.

Sie erhalten das gleiche Verhalten, wenn Sie tailnach den letzten Zeilen einer Dateisystem-Pipe fragen . Erwägen:

$ mkfifo test.pipe
$ tail test.pipe

stdbufDas wahrgenommene Problem zu umgehen war ein nobler Versuch. Die wichtigste Tatsache ist jedoch, dass die E / A-Pufferung nicht die Hauptursache ist: Das Fehlen eines bestimmten Dateiendes ist die Ursache. Wenn Sie den Quellcode von tail.c auschecken , wird der file_linesFunktionskommentar wie folgt angezeigt :

END_POS ist der Dateiversatz von EOF (einer größer als der Versatz des letzten Bytes).

und das ist die Magie. Sie benötigen ein Dateiende, damit tail in jeder Konfiguration funktioniert. headhat diese Einschränkung nicht, es braucht nur einen Dateianfang (was es vielleicht nicht hat, versuchen Sie es head test.pipe). Stream-orientierte Tools mögen sedund awkbrauchen weder einen Anfang noch ein Ende der Datei: Sie arbeiten mit Puffern.

Bischof
quelle
37

tail -f's Schwanz ist eigentlich etwas Unbekanntes in der Gegenwart, also woher soll der nächste es tailwissen. Auf der anderen Seite ist tail -fder Kopf schon bekannt und könnte daher verarbeitet werden.

Oder einfacher ausgedrückt: tailIst relativ zum Ende der Datei, aber der Ausgabestream tail -fhat keine EOF erhalten (zumindest nicht vor dessen Beendigung).

Wenn Sie die erste finden tail‚s pid und töten sie, sollten Sie dann die Ausgabe von dem zweiten sehen.

Ghanima
quelle
21

Technische Antwort

Wenn ein Stream als Eingabe ausgeführt wird, wird tailein n-zeiliger Puffer beibehalten, der beim Lesen des Streams gefüllt wird. Diese Zeilen können jedoch erst ausgegeben werden, wenn das Ende des Streams erreicht ist, dh, EOFwenn versucht wird, von der Eingabe zu lesen, wird ein spezieller Code empfangen Strom. Der Aufruf tail -fwird nicht beendet, daher wird er seinen Stream niemals schließen, was es unmöglich macht, z. B. die 10 letzten Zeilen dieses Streams zurückzugeben.

sleblanc
quelle
3

Die Funktion von tailist, den letzten Teil - "Schwanz" - der Eingabe oder Datei anzuzeigen. (Die Option -fhandelt davon, was später getan wird, sodass dies hier nicht relevant ist.)

Denken wir über eine Datei nach:

Was ist der letzte Teil einer Datei ?
Angenommen, es sind die letzten n Zeilen einer Datei.

Wenn wir die Zeile ider Eingabedatei lesen , wie sollen wir entscheiden, ob sie gedruckt werden soll oder nicht?
Wir wissen nicht, ob es im letzten Teil ist - weil wir nicht wissen, welche die letzte Zeile sein wird. Also können wir es jetzt nicht drucken.

Wir müssen die Linie so lange beibehalten, bis klar wird, dass sie Teil der letzten nLinien ist oder nicht mehr dazu gehören kann, weil wir nweitere Linien kennen

Wenn wir jetzt zum Ende der Datei kommen , wissen wir, dass die letzten nZeilen, die wir behalten haben, tatsächlich die letzten nZeilen der Datei sind.

Nun im Fall von

tail -f /etc/passwd | tail

Der erste tailliest die Datei und wartet dann, bis er weitere Daten daraus abruft, um diese auch auszuschreiben. Daher wird dem zweiten Ende kein Dateiende signalisiert, wenn das Ende der gelesenen Datei erreicht ist. Andernfalls wird der zweite tail Benutzer nie über das Ende der Datei informiert , sodass er nie herausfinden kann, welche der letzten Zeilen gedruckt werden sollen.

Volker Siegel
quelle