Erzwingen Sie die Zeilenpufferung von stdout, wenn Sie zum Abschlag leiten


116

Normalerweise stdoutist zeilengepuffert. Mit anderen Worten, solange Ihr printfArgument mit einer neuen Zeile endet, können Sie erwarten, dass die Zeile sofort gedruckt wird. Dies scheint nicht zu gelten, wenn Sie eine Pipe zum Umleiten verwenden tee.

Ich habe ein C ++ - Programm, adas Zeichenfolgen ausgibt, die immer abgeschlossen \nsind stdout.

Wenn es von selbst ausgeführt wird ( ./a), wird alles wie erwartet korrekt und zur richtigen Zeit gedruckt. Wenn ich es jedoch an tee( ./a | tee output.txt) weitergebe, wird nichts gedruckt, bis es beendet wird, was den Verwendungszweck zunichte macht tee.

Ich weiß, dass ich das Problem beheben kann, indem ich fflush(stdout)nach jedem Druckvorgang im C ++ - Programm ein hinzufüge . Aber gibt es einen saubereren und einfacheren Weg? Gibt es einen Befehl, den ich zum Beispiel ausführen kann, der die Zeilenpufferung erzwingen würde stdout, selbst wenn eine Pipe verwendet wird?

Antworten:


66

Versuchen Sie, unbufferwelches Teil des expectPakets ist. Möglicherweise haben Sie es bereits auf Ihrem System.

In Ihrem Fall würden Sie es so verwenden:

./a | unbuffer -p tee output.txt

( -pist für den Pipeline-Modus, in dem der Unpuffer aus stdin liest und ihn in den restlichen Argumenten an den Befehl übergibt)


Danke, das hat funktioniert, obwohl ich expectmich selbst kompilieren musste, da unbufferes in OS X nicht standardmäßig enthalten zu sein scheint.
houbysoft

@houbysoft: Ich bin froh, dass es für dich funktioniert hat. unbufferist nur ein kleines Skript, daher hätten Sie das gesamte Paket nicht neu kompilieren müssen.
Bis auf weiteres angehalten.

Ja, wahrscheinlich nicht, aber es ./configure && makedauerte ungefähr 10 Sekunden und dann bin ich einfach unbufferzu /usr/local/bin:)
houbysoft

3
Ich habe es auf meinem Mac (10.8.5) über Brew installiert: Brew Install erwarten --with-Brewed-TK
Nils

2
FWIW, da der Puffer etwas verwirrend ist, ist die relevante Struktur unbuffer {commands with pipes/tee}.
Gefälschter Name

127

Du kannst es versuchen stdbuf

$ stdbuf -o 0 ./a | tee output.txt

(großer) Teil der Manpage:

  -i, --input=MODE   adjust standard input stream buffering
  -o, --output=MODE  adjust standard output stream buffering
  -e, --error=MODE   adjust standard error stream buffering

If MODE is 'L' the corresponding stream will be line buffered.
This option is invalid with standard input.

If MODE is '0' the corresponding stream will be unbuffered.

Otherwise MODE is a number which may be followed by one of the following:
KB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.
In this case the corresponding stream will be fully buffered with the buffer
size set to MODE bytes.

Beachten Sie jedoch Folgendes:

NOTE: If COMMAND adjusts the buffering of its standard streams ('tee' does
for e.g.) then that will override corresponding settings changed by 'stdbuf'.
Also some filters (like 'dd' and 'cat' etc.) dont use streams for I/O,
and are thus unaffected by 'stdbuf' settings.

Sie laufen nicht stdbufweiter tee, Sie laufen weiter a, daher sollte dies Sie nicht betreffen, es sei denn, Sie legen die Pufferung der aStreams in ader Quelle fest.

Auch stdbufist nicht POSIX, sondern Teil von GNU-Coreutils.


3
Danke, aber dies scheint unter OS X nicht verfügbar zu sein (die Frage ist mit osx-lion gekennzeichnet).
Houbysoft

2
@houbysoft - Ich bin ziemlich sicher, dass GNU-Tools unter OS X installiert werden können
jordanm

1
@ Jordanm: vielleicht, aber die Installation der gesamten GNU-Tools scheint ein Overkill für diese ...
Houbysoft

1
Diese Antwort wurde verbessert, da sie stdbufbereits in den von uns verwendeten Centos Linux-Distributionen verfügbar ist und unbuffernicht. Vielen Dank!
Huw Walters

6
Für Python-Skripte funktioniert stdbuf nicht, aber Sie können die -uPufferung auf Pythons Seite deaktivieren:python3 -u a.py | tee output.txt
Honza

27

Sie können auch versuchen, Ihren Befehl in einem Pseudo-Terminal mit dem scriptBefehl auszuführen (der die zeilengepufferte Ausgabe an die Pipe erzwingen soll)!

script -q /dev/null ./a | tee output.txt     # Mac OS X, FreeBSD
script -c "./a" /dev/null | tee output.txt   # Linux

Beachten Sie, dass der scriptBefehl den Exit-Status des umschlossenen Befehls nicht zurückgibt.


3
script -t 1 /path/to/outputfile.txt ./afunktionierte hervorragend für meinen Anwendungsfall. Es überträgt alle Ausgaben live an outputfile.txtund druckt sie gleichzeitig auf die Standardausgabe Ihrer Shell. Musste nicht verwendentee
Peter Berg

26

Sie können setlinebuf von stdio.h verwenden.

setlinebuf(stdout);

Dies sollte die Pufferung in "zeilengepuffert" ändern.

Wenn Sie mehr Flexibilität benötigen, können Sie setvbuf verwenden.


8
Ich frage mich, warum diese Lösung so wenige positive Stimmen hat. Dies ist die einzige Lösung, die den Anrufer nicht belastet.
Oxygene

1
Beachten Sie, dass dies nicht Standard C (oder sogar POSIX) ist. Es ist wahrscheinlich besser zu verwenden setvbuf(stdout, NULL, _IOLBF, 0), was genau gleichwertig ist.
Rvighne

Dies hat mein Problem unter OS X Catalina mit einem C ++ - Programm behoben, das printf () war und das ich zum Tee leitete, aber die Ausgabe erst sah, als das Programm beendet war.
jbaxter

2

Wenn Sie stattdessen die C ++ - Stream-Klassen verwenden, handelt es sich bei jedem std::endl um einen impliziten Flush. Ich denke, beim Drucken im C-Stil ist die von Ihnen vorgeschlagene Methode ( fflush()) der einzige Weg.


4
Dies ist leider nicht wahr. Sie können das gleiche Verhalten mit c ++ std :: cout beobachten, auch wenn Sie std :: endl oder std :: flush verwenden. Die Pufferung erfolgt oben und die einfachste Lösung unter Linux scheint setlinebuf (stdout) zu sein. als allererste Zeile in main (), wenn Sie der Autor des Programms sind und die anderen oben genannten Lösungen verwenden, wenn Sie den Quellcode nicht ändern können.
Oxygene

1
@oxygene Das ist nicht wahr. Ich habe es versucht und endl spült den Puffer beim Piping zum Tee (im Gegensatz zu printf). Code : #include <iostream> #include <unistd.h> int main(void) { std::cout << "1" << std::endl; sleep(1); std::cout << "2" << std::endl; }. endl leert
Curtis Yallop
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.