it-swarm.com.de

Schalten Sie die Pufferung im Rohr aus

Ich habe ein Skript, das zwei Befehle aufruft:

long_running_command | print_progress

Der long_running_command Gibt den Fortschritt aus, aber ich bin damit nicht zufrieden. Ich benutze print_progress, Um es schöner zu machen (nämlich ich drucke den Fortschritt in einer einzigen Zeile).

Das Problem : Durch das Verbinden einer Pipe mit stdout wird auch ein 4K-Puffer aktiviert, sodass das Druckprogramm Nizza nichts ... nichts ... nichts ... als Ganzes erhält lot ... :)

Wie kann ich den 4K - Puffer für long_running_command Deaktivieren (nein, ich habe keine Quelle)?

409
Aaron Digulla

Sie können den Befehl unbuffer verwenden (der Teil des Pakets expect ist), z.

unbuffer long_running_command | print_progress

unbuffer verbindet sich mit long_running_command über ein Pseudoterminal (pty), wodurch das System es als interaktiven Prozess behandelt und daher nicht die 4-kiB-Pufferung in der Pipeline 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.

unbuffer x | unbuffer -p y | z
262
cheduardo

Eine andere Möglichkeit, diese Katze zu häuten, ist die Verwendung des Programms stdbuf , das Teil des Coreutils GNU Coreutils) ist (FreeBSD hat auch ein eigenes).

stdbuf -i0 -o0 -e0 command

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:

stdbuf -oL -eL command

Beachten Sie, dass dies nur für die Pufferung stdio (printf(), fputs()...) für dynamisch verknüpfte Anwendungen funktioniert und nur dann, wenn diese Anwendung die Pufferung nicht anderweitig anpasst von seinen Standard-Streams für sich, obwohl dies die meisten Anwendungen abdecken sollte.

474
a3nm

Eine weitere Möglichkeit, den Zeilenpuffer-Ausgabemodus für long_running_command verwendet den Befehl script, mit dem Ihr long_running_command in einem Pseudo-Terminal (pty).

script -q /dev/null long_running_command | print_progress      # FreeBSD, Mac OS X
script -c "long_running_command" /dev/null | print_progress    # Linux
78
chad

Für grep, sed und awk können Sie die Zeilenpufferung der Ausgabe erzwingen. Sie können verwenden:

grep --line-buffered

Erzwingen, dass die Ausgabe zeilengepuffert wird. Standardmäßig ist die Ausgabe zeilengepuffert, wenn die Standardausgabe ein Terminal und der Block anderweitig gepuffert ist.

sed -u

Machen Sie die Ausgabezeile gepuffert.

Weitere Informationen finden Sie auf dieser Seite: http://www.perkin.org.uk/posts/how-to-fix-stdio-buffering.html

69
yaneku

Wenn es ein Problem mit der libc gibt, die ihre Pufferung/Löschung ändert, wenn die Ausgabe nicht an ein Terminal geht, sollten Sie socat versuchen. Sie können einen bidirektionalen Stream zwischen nahezu allen E/A-Mechanismen erstellen. Eines davon ist ein gespaltenes Programm, das mit einer Pseudo tty spricht.

 socat EXEC:long_running_command,pty,ctty STDIO 

Was es tut ist

  • erstellen Sie eine Pseudo-Tty
  • fork long_running_command mit der Slave-Seite des Pty als stdin/stdout
  • stellen Sie einen bidirektionalen Stream zwischen der Masterseite des Pty und der zweiten Adresse her (hier ist es STDIO).

Wenn Sie die gleiche Ausgabe wie long_running_command Erhalten, können Sie mit einer Pipe fortfahren.

Edit: Wow Habe die ungepufferte Antwort nicht gesehen! Nun, socat ist sowieso ein großartiges Werkzeug, also könnte ich diese Antwort einfach hinterlassen

52
shodanex

Sie können verwenden

long_running_command 1>&2 |& print_progress

Das Problem ist, dass libc beim Stdout auf dem Bildschirm einen Zeilenpuffer und beim Stdout auf eine Datei einen vollständigen Puffer erstellt. Aber kein Puffer für stderr.

Ich glaube nicht, dass es das Problem mit dem Pipe-Puffer ist, es geht nur um die Pufferrichtlinie von libc.

20
Wang HongQin

Früher war und ist es wahrscheinlich immer noch so, dass die Standardausgabe beim Schreiben in ein Terminal standardmäßig zeilengepuffert ist. Wenn eine neue Zeile geschrieben wird, wird die Zeile in das Terminal geschrieben. Wenn die Standardausgabe an eine Pipe gesendet wird, wird 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 Quelle des Problems. Ich bin mir nicht sicher, ob Sie viel tun können, um das Problem zu beheben, ohne das Programmschreiben in die Pipe zu ändern. Sie können die Funktion setvbuf() mit dem Flag _IOLBF Verwenden, um stdout bedingungslos in den zeilengepufferten Modus zu versetzen. Aber ich sehe keinen einfachen Weg, dies in einem Programm durchzusetzen. Oder das Programm kann fflush() an geeigneten Stellen (nach jeder Ausgabezeile) ausführen, es gilt jedoch der gleiche Kommentar.

Ich nehme an, wenn Sie die Pipe durch ein Pseudo-Terminal ersetzen würden, würde die Standard-E/A-Bibliothek denken, dass die Ausgabe ein Terminal ist (weil es sich um eine Art Terminal handelt) und den Zeilenpuffer automatisch. Das ist jedoch eine komplexe Art, mit Dingen umzugehen.

11

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:

stdbuf -oL tail -f /var/log/messages | tee -a /home/your_user_here/logs.txt

Dadurch werden die Protokolle in Echtzeit ausgegeben und im logs.txt Datei und der Puffer wirken sich nicht mehr auf die tail -f Befehl.

7
Marin Nedea

Ich glaube nicht, dass das Problem bei der Pfeife liegt. Es hört sich so an, als würde Ihr lang laufender Prozess seinen eigenen Puffer nicht häufig genug leeren. Das Ändern der Puffergröße der Pipe wäre ein Hack, um sie zu umgehen, aber ich denke nicht, dass dies möglich ist, ohne den Kernel neu zu erstellen - etwas, das Sie als Hack nicht tun möchten, da es wahrscheinlich viele andere Prozesse beeinflusst.

5
anon

In ähnlicher Weise wie chads Antwort können Sie ein kleines Skript wie das folgende schreiben:

# save as ~/bin/scriptee, or so
script -q /dev/null sh -c 'exec cat > /dev/null'

Verwenden Sie dann diesen Befehl scriptee als Ersatz für tee.

my-long-running-command | scriptee

Leider kann ich nicht scheinen, dass eine solche Version unter Linux perfekt funktioniert, daher scheint sie auf Unixe im BSD-Stil beschränkt zu sein.

Unter Linux ist dies in der Nähe, aber Sie erhalten Ihre Eingabeaufforderung nicht zurück, wenn sie beendet ist (bis Sie die Eingabetaste usw. drücken) ...

script -q -c 'cat > /proc/self/fd/1' /dev/null
3
jwd

Laut dieser Beitrag hier könnten Sie versuchen, das Pipe-Ulimit auf einen einzelnen 512-Byte-Block zu reduzieren. Die Pufferung wird sicherlich nicht deaktiviert, aber 512 Bytes sind weit weniger als 4K: 3

2
RAKK

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 echo seine Argumente in den Eingabestream von Shell_executable eingefügt hat.

1
jaggedsoft

Laut this scheint die Pipe-Puffergröße im Kernel festgelegt zu sein und würde erfordern, dass Sie Ihren Kernel neu kompilieren, um ihn zu ändern.

0
second