Angesichts dieses minimalen Beispiels
( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; )
es gibt LINE 1
und dann, nach einer Sekunde, Ausgänge LINE 2
, wie erwartet .
Wenn wir das weiterleiten grep LINE
( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; ) | grep LINE
Das Verhalten ist das gleiche wie im vorherigen Fall, wie erwartet .
Wenn wir dies alternativ weiterleiten cat
( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; ) | cat
Das Verhalten ist wieder das gleiche, wie erwartet .
Allerdings , wenn wir Rohr grep LINE
, und dann cat
,
( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; ) | grep LINE | cat
Es erfolgt keine Ausgabe, bis eine Sekunde verstrichen ist, und beide Zeilen erscheinen sofort auf der Ausgabe, was ich nicht erwartet hatte .
Warum passiert das und wie kann ich die letzte Version so einstellen, dass sie sich wie die ersten drei Befehle verhält?
cat
verkettet Dateien. Was versuchst du zu tun, indem du hineinpfeifstcat
?cat
einfach einlesenstdin
und ausgebenstdout
. Natürlich habe ich mir diese Frage mit einer Menge komplexer Sachen anstelle vonecho
und ausgedachtcat
, aber diese erwiesen sich als irrelevant, da das Problem mit viel einfacheren Beispielen auftaucht.Antworten:
Wenn die
grep
Ausgabe von (mindestens GNU) kein Terminal ist, puffert sie die Ausgabe. Dies ist der Grund für das Verhalten, das Sie sehen. Sie können dies entweder mit der GNUgrep
---line-buffered
Option deaktivieren :oder das
stdbuf
Dienstprogramm:Das Ausschalten der Pufferung in der Pipe hat mehr zu diesem Thema.
quelle
Vereinfachte Erklärung
Wie bei vielen Dienstprogrammen
grep
variiert auch dies, da es sich nicht um eine Besonderheit eines Programms handelt, die Standardausgabe zwischen Zeilen- und Vollpufferung . Im ersteren Fall puffert die C-Bibliothek die Ausgabedaten im Speicher, bis entweder der Puffer, der diese Daten enthält, gefüllt ist oder ein Zeilenvorschubzeichen hinzugefügt ist (oder das Programm sauber endet), woraufhin es aufruftwrite()
, den Pufferinhalt tatsächlich zu schreiben. Im letzteren Fall löst nur der In-Memory-Puffer, der voll wird (oder das Programm endet sauber), den auswrite()
.Detailliertere Erklärung
Dies ist die bekannte, aber leicht falsche Erklärung. Tatsächlich wird die Standardausgabe in der GNU C-Bibliothek und der BSD C-Bibliothek nicht zeilen-, sondern intelligent gepuffert . Standardausgabe ist auch gespült , wenn Standard - Leseeingangs erschöpft seine in-Speicherpuffer (der Vor-Lese - Eingang) und die C - Bibliothek aufrufen
read()
etwas mehr Eingabe zu holen und es ist , den Anfang einer neuen Zeile zu lesen. (Ein Grund dafür ist, ein Deadlock zu verhindern, wenn sich ein anderes Programm mit beiden Enden eines Filters verbindet und erwartet, dass es zeilenweise abwechselnd auf den Filter schreiben und von ihm lesen kann, wie "Coprozesse" in GNUawk
beispielsweise.)C Bibliothekseinfluss
grep
und die anderen Dienstprogramme tun dies - oder genauer gesagt die C-Bibliotheken, die sie verwenden -, da dies eine definierte Funktion der Programmierung in der C-Sprache ist - basierend auf dem, was sie als Standardausgabe erkennen. Wenn (und nur wenn) es sich nicht um ein interaktives Gerät handelt, wählen sie die vollständige Pufferung, andernfalls wählen sie die intelligente Pufferung. Eine Pipe wird nicht als interaktives Gerät betrachtet, da die Definition eines interaktiven Geräts, zumindest in der Welt von Unix und Linux, im Wesentlichen dieisatty()
Rückgabe von true für den entsprechenden Dateideskriptor ist.Problemumgehungen zum Deaktivieren der vollständigen Pufferung
Einige Dienstprogramme wie
grep
haben eigenwillige Optionen wie--line-buffered
diese, die diese Entscheidung ändern, was, wie Sie sehen, falsch benannt ist. Aber ein verschwindend kleiner Teil der Filterprogramme, die man verwenden könnte, hat tatsächlich eine solche Option.Im Allgemeinen kann man Tools verwenden, die in die spezifischen Interna der C-Bibliothek eingreifen und deren Entscheidungsfindung ändern (die Sicherheitsprobleme aufweisen, wenn das zu ändernde Programm eine Set-UID hat, und die auch für bestimmte C-Bibliotheken spezifisch sind und tatsächlich sind) B. Programme, die in der Programmiersprache C geschrieben sind oder über der Programmiersprache C liegen), oder Tools
ptybandage
, die die Interna des Programms nicht ändern, sondern einfach ein Pseudoterminal als Standardausgabe einfügen, damit die Entscheidung als "interaktiv" ausfällt beeinflussen dies.Weitere Lektüre
quelle
grep
der zugrunde liegenden Bibliotheksaufrufe,setbuf
/setvbuf
. Ich kenne keine verlässliche Online-Referenz für den C-Standard, aber z. B. die Linux- und FreeBSD-Handbuchseiten zusammen mit der POSIX-Beschreibung vonsetvbuf
call it "line buffered". Sogar die symbolische Konstante dafür ist_IOLBF
.Verwenden
Damit grep nicht mehr als eine Zeile gleichzeitig puffert.
quelle