Wie kann man stdout und stderr in eine Datei umleiten und stderr zur Konsole anzeigen?

18

Ich kann zu einer Akte umleiten und T-Stück benutzen; auf einer grundlegenden Ebene. So

$ alias outanderr='bash -c "echo stdout >&1; echo stderr >&2"'
# A fake "application" displaying both output and error messages.

$ outanderr 1>file      # redirect stdout to a file, display stderr
stderr

$ outanderr 2>file      # redirect stderr to a file, display stdout
stdout

$ outanderr 1>file 2>&1 # redirect both to a file, display nothing

$ outanderr | tee file; echo "-- file contents --" && cat file
# redirect stdout to a file, display both (note: order is messed up)
stderr
stdout
-- file contents --
stdout

$ outanderr 2>&1 | tee file; echo "-- file contents --" && cat file
# redirect both to a file, display both
stdout
stderr
-- file contents --
stdout
stderr

Die Frage ist, was anstelle der Fragezeichen zu schreiben ist, um die folgende Ausgabe zu erhalten:

$ outanderr ???; echo "-- file contents --" && cat file
# redirect both to a file, display stderr
stderr
-- file contents --
stdout
stderr

Constaints:

  • Vorausgesetzt, bash.
  • Die Bestellung sollte in der Datei aufbewahrt werden.
  • stderr-Inhalte werden zeilenweise in Echtzeit angezeigt, dh ohne Pufferung.
  • Es können separate Skriptdateien verwendet werden.
  • Magie kann notwendig sein.
TWiStErRob
quelle
Wie viel Kontrolle über das outanderrProgramm haben Sie?
Kevin
1
@ Kevin Ich denke, die Frage ist allgemeiner als das. Hier outanderrist nur ein Alias, der eine Zeile an stdout und eine andere an stderr ausgibt. Die Idee (wenn es möglich ist) ist, eine generische Lösung zu erstellen, die mit jedem Programm funktionieren kann, ohne sie zu ändern.
Lgeorget
@lgeorget Ich verstehe das, aber ich glaube nicht, dass es möglich ist, alle Einschränkungen in einer generischen Lösung strikt zu erfüllen, also habe ich geprüft, ob wir eine bestimmte bekommen könnten.
Kevin
@Igeorget ist richtig.
TWiStErRob 20.06.13

Antworten:

12
2>&1 >>outputfile | tee --append outputfile

Zum einfachen Testen:

echo -n >outputfile; bash -c "echo stdout >&1; echo stderr >&2" 2>&1 >>outputfile |
  tee --append outputfile; echo "outputfile:"; cat outputfile

Bearbeiten 1:

Dies funktioniert, indem stdout (nur) in die Datei geschrieben wird, sterr stdout so gemacht wird, dass es durch die Pipe geht, und tee seine Ausgabe in dieselbe Datei schreibt.

Beide Schreibvorgänge müssen im Anfügemodus ( >>anstelle von >) ausgeführt werden, da sich sonst beide Ausgaben gegenseitig überschreiben würden.

Da die Pipe ein Puffer ist, kann nicht garantiert werden, dass die Ausgabe in der richtigen Reihenfolge in der Datei angezeigt wird. Dies würde sich auch dann nicht ändern, wenn eine Anwendung mit beiden Dateideskriptoren (zwei Pipes) verbunden wäre. Für eine garantierte Bestellung müssten beide Ausgänge denselben Kanal durchlaufen und jeweils markiert sein. Oder du brauchst ein paar wirklich ausgefallene Sachen:

  1. Wenn sowohl stdout als auch stderr in eine Datei (nicht dieselbe Datei!) Umgeleitet wurden und sich beide Dateien auf einem FUSE-Volume befanden, konnte das FUSE-Modul jeden einzelnen Schreibvorgang mit einem Zeitstempel markieren, sodass eine zweite Anwendung die Daten korrekt sortieren und kombinieren konnte für die echte Ausgabedatei. Oder Sie markieren die Daten nicht, sondern lassen das Modul die kombinierte Ausgabedatei erstellen. Höchstwahrscheinlich gibt es noch kein FUSE-Modul, das dies tut ...
  2. Sowohl stdout als auch stderr könnten angesprochen werden /dev/null. Die Ausgaben der Anwendung werden durch Durchlaufen getrennt strace -f -s 32000 -e trace=write. In diesem Fall müssten Sie die Fluchtung rückgängig machen. Es ist unnötig zu erwähnen, dass die Anwendung nicht schneller ausgeführt wird, wenn sie nachverfolgt wird.
  3. Möglicherweise kann dasselbe erreicht werden, indem ein vorhandenes, einfaches FUSE-Modul verwendet und das Modul anstelle der Anwendung nachverfolgt wird. Dies ist möglicherweise schneller als das Verfolgen der Anwendung, da das Modul (oder besser gesagt: if) wahrscheinlich viel weniger Systemaufrufe als die Anwendung hat.
  4. Wenn die Anwendung selbst geändert werden kann: Die Anwendung kann nach jeder Ausgabe gestoppt werden (aber ich denke, dies ist nur von innen möglich) und erst nach Empfang des Signals s (SIGUSR1 oder SIGCONT) fortgesetzt werden. Die Anwendung, die aus der Pipe liest, müsste sowohl die Pipe als auch die Datei auf neue Daten prüfen und das Signal nach jedem neuen Datum senden. Je nach Art der Anwendung kann dies schneller oder sogar langsamer sein als die Strace-Methode. FUSE wäre die Lösung mit maximaler Geschwindigkeit.
Hauke ​​Laging
quelle
1
Bah. Ich schreibe gerade genau die gleiche Antwort, warum nicht?
Kevin
2
NB das hat eine Rennbedingung, die die Möglichkeit des Austauschs / der falschen Linien einführt, aber ich denke nicht, dass das vermieden werden kann.
Kevin
1
@ Kevin Das passiert den Besten von uns, ich habe schon früher darunter gelitten und fast um ein Feature gebeten "Zeig mir, dass jemand schreibt" (was allerdings kompliziert wäre). Es scheint mir, dass die Race-Bedingung nur auftritt, wenn ein Schreibvorgang in die Datei (stdout) nach einem Schreibvorgang in die Pipeline erfolgt.
Hauke ​​Laging
Würde das nicht beides stdoutund stderrnach senden tee, oder vermisse ich etwas? Ich denke, die Anforderung des OP ist tee stderrnur zu.
Joseph R.
@ JosephR. Sie haben das nicht ausprobiert?
Hauke ​​Laging