Aus dem stdout
Handbuchseite:
Der Stream stderr ist ungepuffert. Der Stream stdout ist zeilengepuffert, wenn er auf ein Terminal zeigt .Teilzeilen werden nicht angezeigt, bis fflush(3) oder exit(3) aufgerufen oder eine neue Zeile ausgegeben wird.
Fazit:Sofern es sich bei der Ausgabe nicht um ein Terminal handelt, wird Ihr Programm seine Standardausgabe standardmäßig im vollständig gepufferten Modus haben. Dies bedeutet im Wesentlichen, dass Daten in großen Blöcken ausgegeben werden, anstatt Zeile für Zeile, geschweige denn Zeichen für Zeichen.
Möglichkeiten, dies zu umgehen:
-
Reparieren Sie Ihr Programm:Wenn Sie eine Echtzeitausgabe benötigen, müssen Sie Ihr Programm reparieren. In C können Sie
fflush(stdout)
verwenden nach jeder Ausgabeanweisung odersetvbuf()
um den Pufferungsmodus der Standardausgabe zu ändern. Für Python gibt essys.stdout.flush()
von sogar einigen der Vorschläge hier. -
Verwenden Sie ein Dienstprogramm, das von einem PTY aufzeichnen kann, anstatt direkte stdout-Umleitungen. GNU Screen kann dies für Sie erledigen:
screen -d -m -L python test.py
wäre ein Anfang. Dadurch wird die Ausgabe Ihres Programms in einer Datei namens
screenlog.0
protokolliert (oder ähnlich) in Ihrem aktuellen Verzeichnis mit einer Standardverzögerung von 10 Sekunden, und Sie könnenscreen
verwenden um eine Verbindung zu der Sitzung herzustellen, in der Ihr Befehl ausgeführt wird, um Eingaben bereitzustellen oder ihn zu beenden. Die Verzögerung und der Name der Protokolldatei können in einer Konfigurationsdatei oder manuell geändert werden, sobald Sie sich mit der Hintergrundsitzung verbinden.
BEARBEITEN:
Auf den meisten Linux-Systemen gibt es eine dritte Problemumgehung:Sie können den LD_PRELOAD
verwenden Variable und eine vorinstallierte Bibliothek, um ausgewählte Funktionen der C-Bibliothek zu überschreiben und sie zum Setzen von stdout
zu verwenden Puffermodus, wenn diese Funktionen von Ihrem Programm aufgerufen werden. Diese Methode kann funktionieren, hat aber eine Reihe von Nachteilen:
-
Es funktioniert überhaupt nicht mit statischen ausführbaren Dateien
-
Es ist zerbrechlich und ziemlich hässlich.
-
Es funktioniert überhaupt nicht mit ausführbaren SUID-Dateien - der dynamische Loader weigert sich, den
LD_PRELOAD
zu lesen Variable beim Laden solcher ausführbaren Dateien aus Sicherheitsgründen. -
Es ist zerbrechlich und ziemlich hässlich.
-
Es erfordert, dass Sie eine Bibliotheksfunktion finden und überschreiben, die nach von Ihrem Programm aufgerufen wird es setzt zunächst den
stdout
Puffermodus und vorzugsweise vorher jede Ausgabe.getenv()
ist eine gute Wahl für viele Programme, aber nicht für alle. Möglicherweise müssen Sie allgemeine E/A-Funktionen wieprintf()
überschreiben oderfwrite()
- Wenn es hart auf hart kommt, müssen Sie möglicherweise nur alle Funktionen überschreiben, die den Puffermodus steuern, und eine Sonderbedingung fürstdout
einführen . -
Es ist zerbrechlich und ziemlich hässlich.
-
Es ist schwer sicherzustellen, dass es keine unerwünschten Nebenwirkungen gibt. Um dies richtig zu machen, müssten Sie sicherstellen, dass nur
stdout
betroffen ist und dass Ihre Überschreibungen den Rest des Programms nicht zum Absturz bringen, wenn z.stdout
ist geschlossen. -
Habe ich erwähnt, dass es zerbrechlich und ziemlich hässlich ist?
Das heißt, der Prozess ist relativ einfach. Sie legen eine C-Datei an, z. linebufferedstdout.c
die Ersetzungsfunktionen:
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>
char *getenv(const char *s) {
static char *(*getenv_real)(const char *s) = NULL;
if (getenv_real == NULL) {
getenv_real = dlsym(RTLD_NEXT, "getenv");
setlinebuf(stdout);
}
return getenv_real(s);
}
Dann kompilieren Sie diese Datei als gemeinsames Objekt:
gcc -O2 -o linebufferedstdout.so -fpic -shared linebufferedstdout.c -ldl -lc
Dann setzt du den LD_PRELOAD
Variable, um sie zusammen mit Ihrem Programm zu laden:
$ LD_PRELOAD=./linebufferedstdout.so python test.py | tee -a test.out
0
1000
2000
3000
4000
Wenn Sie Glück haben, wird Ihr Problem ohne Unglück gelöst Nebenwirkungen.
Sie können den LD_PRELOAD
einstellen Bibliothek in der Shell, falls erforderlich, oder geben Sie diese Bibliothek sogar systemweit an (definitiv NICHT empfohlen) in /etc/ld.so.preload
.
Haben Sie darüber nachgedacht, zum Abschlag zu leiten?
./program | tee a.txt
Allerdings funktioniert auch tee nicht, wenn "program" nichts nach stdout schreibt, bis es fertig ist. Die Effektivität hängt also stark davon ab, wie sich Ihr Programm verhält.
Wenn Sie versuchen, das Verhalten eines bestehenden Programms zu ändern, versuchen Sie es mit stdbuf (Teil von Coreutils, anscheinend beginnend mit Version 7.5).
Dies puffert stdout bis zu einer Zeile:
stdbuf -oL command > output
Dies deaktiviert die stdout-Pufferung insgesamt:
stdbuf -o0 command > output