Ich führe ein Skript über SSH folgendermaßen aus:
ssh user@host 'bash -s' < ./script.sh
das Problem besteht darin, dass die Ausgabe, die ich erhalte, manchmal nicht korrekt ist und die Zeilen gemischt sind.
In meinem Fall führt das Skript nicht viel Neues aus, die normale Ausgabe ist etwa:
...
Note: Ignoring non-mail file: foobar
Note: Ignoring non-mail file: foobar
Note: Ignoring non-mail file: foobar
Processed 93 total files in almost no time.
No new mail.
aber manchmal ist die Ausgabe etwa:
...
Note: Ignoring non-mail file: foobar
Note: Ignoring non-mail file: foobar
Processed 93 total files in almost no time.
No new mail.
Note: Ignoring non-mail file: foobar
Note: Ignoring non-mail file: foobar
und das ist mit Sicherheit nicht die tatsächliche Ausgabe von notmuch new
, der Befehl endet mit, No new mail
aber es ist, als würde er die Ausgabe über SSH erhalten und nicht zeilenweise.
Warum passiert das?
Antwort1
Pufferung. Wenn wir den Quellcode durchsuchen nachnotmuch
$ find . -name \*.c -exec grep 'Ignoring non-mail file' {} +
./notmuch-new.c: fprintf (stderr, "Note: Ignoring non-mail file: %s\n", filename);
$ find . -name \*.c -exec grep 'No new mail' {} +
./notmuch-new.c: printf ("No new mail.");
$
Einige dieser Nachrichten verwenden die Standardfehlerausgabe (die standardmäßig ungepuffert ist) und einige die Standardausgabe (die standardmäßig zeilen- oder blockgepuffert ist, je nachdem, ob die Standardausgabe an ein Terminal oder an eine Datei erfolgt). Dieses Verhalten stammt aus der Standard-C-Bibliothek, siehesetvbuf(3)
für Details. stderr
Nachrichten werden also sofort geschrieben, während die printf
Aufrufe stdout
angezeigt werden... nun, es kommt darauf an.
Das Puffern wird normalerweise von jeder Anwendung einzeln konfiguriert, man kann aber vielleicht mit Dienstprogrammen wie herumspielen stdbuf
(obwohl manche die LD_PRELOAD
von verwendeten Tricks stdbuf
für ziemlich schrecklich halten ...).
Der Unterschied lässt sich lokal leicht reproduzieren, beispielsweise beim Schreiben ins Terminal (zeilenbasierte Pufferung für stdout
):
$ perl -E 'for (1..4) { say "out"; warn "err\n" }'
out
err
out
err
out
err
out
err
$
während stattdessen derselbe Code in eine Datei umgeleitet wird (blockbasierte Pufferung für stdout
):
$ perl -E 'for (1..4) { say "out"; warn "err\n" }' >x 2>&1
$ cat x
err
err
err
err
out
out
out
out
$
ssh
fügt eine zusätzliche Komplikationsebene hinzu, da man möglicherweise auch herausfinden muss, wie es Bytes sammelt, puffert und sendet, und außerdem, notmuch
was ssh
auf dem Clientsystem verkabelt ist …