Ich habe ein Skript, das zwei Befehle aufruft:
long_running_command | print_progress
Der long_running_command
Ausdruck ist ein Fortschritt, aber ich bin unzufrieden damit. Ich verwende print_progress
, um es schöner zu machen (nämlich, ich drucke den Fortschritt in einer einzigen Zeile).
Das Problem: Das Verbinden einer Pipe mit stdout aktiviert auch einen 4K-Puffer, an den das nette Druckprogramm nichts ... nichts ... nichts ... eine ganze Menge ... :)
Wie kann ich den 4K-Puffer für den deaktivieren long_running_command
(nein, ich habe nicht die Quelle)?
Antworten:
Sie können den
unbuffer
Befehl (der Bestandteil desexpect
Pakets ist) verwenden, zunbuffer
stelltlong_running_command
über ein Pseudoterminal (pty) eine Verbindung her, wodurch das System es als interaktiven Prozess behandelt und daher die 4-kiB-Pufferung in der Pipeline nicht verwendet, die die wahrscheinliche Ursache für die Verzögerung ist.Bei längeren Pipelines müssen Sie möglicherweise jeden Befehl (mit Ausnahme des letzten) entpuffern, z
quelle
expect_unbuffer
und ist imexpect-dev
Paket enthalten, nicht imexpect
Paketexpect-dev
bietet beidesunbuffer
undexpect_unbuffer
(ersteres ist ein Symlink zu letzterem). Die Links sind seitexpect 5.44.1.14-1
(2009) verfügbar .Eine andere Möglichkeit, diese Katze zu häuten, ist die Verwendung des
stdbuf
Programms, das Teil von GNU Coreutils ist (FreeBSD hat auch ein eigenes Programm).Dadurch wird die Pufferung für Eingabe, Ausgabe und Fehler vollständig deaktiviert. Für einige Anwendungen ist die Zeilenpufferung aus Leistungsgründen möglicherweise besser geeignet:
Beachten Sie, dass es nur für das
stdio
Puffern (printf()
,fputs()
...) von dynamisch verknüpften Anwendungen funktioniert, und nur, wenn diese Anwendung die Pufferung ihrer Standard-Streams ansonsten nicht selbst anpasst, obwohl dies die meisten Anwendungen abdecken sollte.quelle
sudo stdbuff … command
Werke gefunden, obwohlstdbuff … sudo command
nicht.stdbuf
funktioniert nicht mittee
, datee
die von festgelegten Standardeinstellungen überschrieben werdenstdbuf
. Siehe die Handbuchseite vonstdbuf
.stdbuf
verwendet einenLD_PRELOAD
Mechanismus, um seine eigene dynamisch geladene Bibliothek einzufügenlibstdbuf.so
. Dies bedeutet, dass es mit diesen ausführbaren Dateien nicht funktioniert: mit setuid- oder file-Funktionen, die statisch verknüpft sind und nicht die Standard-libc verwenden. In diesen Fällen ist es besser, die Lösungen mitunbuffer
/script
/ zu verwendensocat
. Siehe auch stdbuf mit setuid / functions .Eine weitere Möglichkeit, den Ausgabemodus für die Zeilenpufferung zu aktivieren,
long_running_command
besteht darin, denscript
Befehl zu verwenden, mit dem Sielong_running_command
in einem Pseudoterminal (pty) ausgeführt werden.quelle
script
es sich um einen so alten Befehl handelt, sollte er auf allen Unix-ähnlichen Plattformen verfügbar sein.-q
unter Linux:script -q -c 'long_running_command' /dev/null | print_progress
stdin
, was es unmöglich macht, ein solches Skriptlong_running_command
im Hintergrund auszuführen , zumindest wenn es über ein interaktives Terminal gestartet wird. Um das Problem zu umgehen, konnte ich stdin von umleiten/dev/null
, da meinelong_running_command
nicht verwendet wirdstdin
.Für
grep
,sed
undawk
Sie können die Ausgabe zwangsweise zeilenweise puffern. Sie können verwenden:Erzwinge die Zeilenpufferung der Ausgabe. Standardmäßig wird die Ausgabe zeilenweise gepuffert, wenn es sich bei der Standardausgabe um eine Klemme handelt und die Ausgabe ansonsten blockweise gepuffert wird.
Ausgabezeile gepuffert machen.
Weitere Informationen finden Sie auf dieser Seite: http://www.perkin.org.uk/posts/how-to-fix-stdio-buffering.html
quelle
Wenn es ein Problem mit der libc gibt, deren Pufferung / Leerung zu ändern, wenn die Ausgabe nicht an ein Terminal geht, sollten Sie socat ausprobieren . Sie können einen bidirektionalen Stream zwischen nahezu allen E / A-Mechanismen erstellen. Eines davon ist ein Forked-Programm, das mit einer Pseudotty spricht.
Was es tut, ist
Wenn dies die gleiche Ausgabe ergibt wie
long_running_command
, können Sie mit einer Pipe fortfahren.Edit: Wow Hab die Unbuffer-Antwort nicht gesehen! Nun, socat ist sowieso ein großartiges Werkzeug, also könnte ich diese Antwort einfach hinterlassen
quelle
socat -u exec:long_running_command,pty,end-close -
hier verwendenSie können verwenden
Das Problem ist, dass libc einen Zeilenpuffer erstellt, wenn stdout auf den Bildschirm zeigt, und einen vollständigen Puffer, wenn stdout auf eine Datei zeigt. Aber kein Puffer für stderr.
Ich glaube nicht, dass es das Problem mit dem Pipe-Buffer ist, es geht nur um die Buffer-Policy von libc.
quelle
zsh
(woher|&
kommt von Csh angepasst) undbash
, wenn Sie dies tuncmd1 >&2 |& cmd2
, beide FD 1 und 2 mit dem äußeren STDOUT verbunden sind. Es verhindert also das Puffern, wenn es sich bei der äußeren Standardausgabe um ein Terminal handelt, aber nur, weil die Ausgabe nicht über die Pipe erfolgt (alsoprint_progress
nichts druckt). Es ist also dasselbe wielong_running_command & print_progress
(außer dass print_progress stdin eine Pipe ist, die keinen Writer hat). Sie können mit imls -l /proc/self/fd >&2 |& cat
Vergleich zu überprüfenls -l /proc/self/fd |& cat
.|&
es2>&1 |
buchstäblich Abkürzung ist. Socmd1 |& cmd2
ist es auchcmd1 1>&2 2>&1 | cmd2
. Somit sind sowohl fd 1 als auch 2 mit dem ursprünglichen stderr verbunden, und es bleibt nichts übrig, was in die Pipe geschrieben wird. (s/outer stdout/outer stderr/g
In meinem vorherigen Kommentar).Früher war und ist es wahrscheinlich immer noch so, dass beim Schreiben der Standardausgabe in ein Terminal standardmäßig eine Zeile gepuffert wird - beim Schreiben einer neuen Zeile wird die Zeile in das Terminal geschrieben. Wenn die Standardausgabe an eine Pipe gesendet wird, ist sie vollständig gepuffert. Daher werden die Daten nur dann an den nächsten Prozess in der Pipeline gesendet, wenn der Standard-E / A-Puffer gefüllt ist.
Das ist die Ursache des Problems. Ich bin mir nicht sicher, ob Sie viel tun können, um das Problem zu beheben, ohne das Programm zu ändern, das in die Pipe geschrieben wird. Sie können die
setvbuf()
Funktion mit dem_IOLBF
Flag verwenden, um bedingungslosstdout
in den zeilengepufferten Modus zu wechseln. Aber ich sehe keine einfache Möglichkeit, dies in einem Programm durchzusetzen. Oder das Programm kannfflush()
an geeigneten Punkten (nach jeder Ausgabezeile), aber der gleiche Kommentar gilt.Ich nehme an, wenn Sie die Pipe durch ein Pseudo-Terminal ersetzen, würde die Standard-E / A-Bibliothek annehmen, dass die Ausgabe ein Terminal ist (da es sich um eine Art Terminal handelt) und den Zeilenpuffer automatisch ausführen. Das ist jedoch eine komplexe Art, mit Dingen umzugehen.
quelle
Ich weiß, dass dies eine alte Frage ist und bereits viele Antworten hatte, aber wenn Sie das Pufferproblem vermeiden möchten, versuchen Sie einfach Folgendes:
Dadurch werden die Protokolle in Echtzeit ausgegeben und in der
logs.txt
Datei gespeichert, und der Puffer hat keine Auswirkungen mehr auf dentail -f
Befehl.quelle
NOTE: If COMMAND adjusts the buffering of its standard streams ('tee' does for example) then that will override corresponding changes by 'stdbuf'.
Ich glaube nicht, dass das Problem bei der Pfeife liegt. Es hört sich so an, als würde Ihr langer Prozess seinen eigenen Puffer nicht häufig genug leeren. Das Ändern der Puffergröße der Pipe wäre ein Hack, um das Problem zu umgehen, aber ich denke nicht, dass dies möglich ist, ohne den Kernel neu zu erstellen - etwas, das Sie nicht als Hack tun möchten, da es wahrscheinlich viele andere Prozesse in Mitleidenschaft zieht.
quelle
In diesem Beitrag können Sie versuchen, das Pipe-Ulimit auf einen einzelnen 512-Byte-Block zu reduzieren. Zwar wird die Pufferung nicht ausgeschaltet, aber 512 Bytes sind weit weniger als 4K: 3
quelle
Ähnlich wie bei der Antwort von Chad können Sie ein kleines Skript wie das folgende schreiben:
Verwenden Sie dann diesen
scriptee
Befehl als Ersatz fürtee
.Leider scheint es mir nicht möglich zu sein, dass eine solche Version unter Linux einwandfrei funktioniert, daher scheint sie auf BSD-Unixe beschränkt zu sein.
Unter Linux ist dies nicht weit entfernt, aber Sie erhalten Ihre Eingabeaufforderung nicht zurück, wenn dies abgeschlossen ist (bis Sie die Eingabetaste usw. drücken) ...
quelle
script
emuliert ein Terminal, also ja, ich glaube, es deaktiviert die Pufferung. Es wird auch jedes Zeichen zurückgesendet, das an dieses gesendet wurde - weshalb im Beispiel ancat
gesendet wird/dev/null
. Für das Programm, das im Inneren ausgeführtscript
wird, spricht es mit einer interaktiven Sitzung. Ich glaube, dass esexpect
in dieser Hinsicht ähnlich ist , aberscript
wahrscheinlich Teil Ihres Basissystems ist.tee
besteht darin, eine Kopie des Streams an eine Datei zu senden. Wo wird die Datei angegebenscriptee
?cat
Befehl durch ersetzentee myfile.txt
, und Sie sollten den gewünschten Effekt erzielen.Ich habe diese clevere Lösung gefunden:
(echo -e "cmd 1\ncmd 2" && cat) | ./shell_executable
Das macht den Trick.
cat
liest zusätzliche Eingaben (bis EOF) und übergibt diese an die Pipe, nachdem dieecho
Argumente in den Eingabestream von eingegeben wurdenshell_executable
.quelle
cat
sieht der die Ausgabe vonecho
; Sie führen nur zwei Befehle in einer Subshell aus und die Ausgabe von beiden wird in die Pipe gesendet. Der zweite Befehl in der Subshell ('cat') liest aus dem Parent / Outer Stdin, deshalb funktioniert es.Nach dieser scheint die Rohrpuffergröße in dem Kernel gesetzt zu werden und würde verlangen , dass Sie Ihren Kernel neu kompilieren zu ändern.
quelle