Wie kann ich stdout in eine Datei und stdout + stderr in eine andere umleiten?

32

Wie kann ich erreichen

cmd >> file1 2>&1 1>>file2

Das heißt, stdout und stderr sollten zu einer Datei (Datei1) umleiten, und nur stdout (Datei2) sollte zu einer anderen umleiten (beide im Anfügemodus).

Swarna Gowri
quelle

Antworten:

41

Das Problem ist, dass wenn Sie Ihre Ausgabe umleiten, sie für die nächste Umleitung nicht mehr verfügbar ist. Sie können teein einer Subshell eine Pipe zu ausführen, um die Ausgabe für die zweite Umleitung beizubehalten:

( cmd | tee -a file2 ) >> file1 2>&1

oder wenn Sie die Ausgabe im Terminal sehen möchten:

( cmd | tee -a file2 ) 2>&1 | tee -a file1

Um das Hinzufügen des Stderr des ersten teezu vermeiden file1, sollten Sie das Stderr Ihres Befehls zu einem Dateideskriptor (z. B. 3) umleiten und dies später erneut zu stdout hinzufügen:

( 2>&3 cmd | tee -a file2 ) >> file1 3>&1
# or
( 2>&3 cmd | tee -a file2 ) 3>&1 | tee -a file1

(danke @ fra-san)

pLumo
quelle
16

Mit zsh:

cmd >& out+err.log > out.log

Im Append-Modus:

cmd >>& out+err.log >> out.log

In zshund unter der Voraussetzung der mult_ioshat Option nicht deaktiviert wurde, wenn ein Dateideskriptor (hier 1) mehrmals zum Schreiben umgeleitet wird, dann die Schale Geräte eines eingebauten, um teedie Ausgabe zu allen Zielen zu duplizieren.

Stéphane Chazelas
quelle
Ich kann nicht herausfinden , was out+errund outbedeuten hier. Dateinamen? Streams umgeleitet werden?
Freitag,
@gronostaj Denken Sie, dass der Befehl lautetcmd >& file1 > file2
Isaac
Diese Lösung behält die Reihenfolge bei, in der die Ausgabe generiert wurde. Um stdout und stderr (in dieser Reihenfolge) tatsächlich zu speichern , benötigen Sie einen anderen Ansatz.
Isaac
1
@Isaac, die Reihenfolge wird nicht unbedingt beibehalten, da die stdout-Ausgabe eine Pipe durchläuft (zu einem Prozess, der sie an jede Datei weiterleitet), während die stderr-Ausgabe direkt an die Datei geht. In jedem Fall sieht es nicht so aus, als ob das OP nach dem Ausgang stdout gefragt hätte, ob der Ausgang stderr nach dem Ausgang stdout kommen soll.
Stéphane Chazelas
3

Sie könnten: stdout taggen (mit einer UNBUFFERED sed, dh:), sed -u ...stderr auch auf stdout gehen lassen (ohne Tag, da es diese Tagging sed nicht durchlaufen hat) und so in der Lage sein, die 2 in der resultierenden Logdatei zu unterscheiden.

Folgendes: ist langsam (Es kann ernsthaft optimiert werden, indem zum Beispiel ein Perl-Skript anstelle von while verwendet wird ...; do ...; done, zum Beispiel, das Subshells und Befehle in jeder Zeile erzeugt!), Seltsam (anscheinend brauche ich die 2 {} Stufen, um in der einen stdout umzubenennen und in der anderen stdout die "durchgefallenen" stderr hinzuzufügen) usw. Aber es ist: ein " Proof of Concept ", der versucht zu halten Die Ausgabe ordnet so viel wie möglich von stdout & stderr an:

#basic principle (some un-necessary "{}" to visually help see the layers):
# { { complex command ;} | sed -e "s/^/TAGstdout/" ;} 2>&1 | read_stdin_and_redispatch

#exemple:
# complex command = a (slowed) ls of several things (some existing, others not)
#  to see if the order of stdout&stderr is kept

#preparation, not needed for the "proof of concept", but needed for our specific exemple setup:
\rm out.file out_AND_err.file unknown unknown2 
touch existing existing2 existing3

#and the (slow, too many execs, etc) "proof of concept":
uniquetag="_stdout_" # change this to something unique, that will NOT appear in all the commands outputs... 
                     # avoid regexp characters ("+" "?" "*" etc) to make it easy to remove with another sed later on.

{
   { for f in existing unknown existing2 unknown2 existing3 ; do ls -l "$f" ; sleep 1; done ;
   } | sed -u -e "s/^/${uniquetag}/" ;
} 2>&1 | while IFS="" read -r line ; do
    case "$line" in
       ${uniquetag}*) printf "%s\n" "$line" | tee -a out_AND_err.file | sed -e "s/^${uniquetag}//" >> out.file ;; 
        *)            printf "%s\n" "$line"       >> out_AND_err.file ;;   
    esac; 
done;

# see the results:
grep "^" out.file out_AND_err.file
Olivier Dulac
quelle
Das ist wirklich schwer zu verstehen. (1) Warum verwenden Sie solch einen komplizierten Anwendungsfall ( ls unknown), um etwas auf stderr zu drucken? >&2 echo "error"wäre in Ordnung. (2) teekann an mehrere Dateien gleichzeitig angehängt werden. (3) Warum nicht einfach catstatt grep "^"? (4) Ihr Skript schlägt fehl, wenn die Ausgabe von stderr mit beginnt _stdout_. (5) Warum?
pLumo
@RoVo: Die ersten 2 kommentierten Zeilen zeigen den Algorithmus, der einfacher ist als das Proof-of-Concept-Beispiel. 1): dies ls loopwird sowohl auf stdout als auch auf stderr ausgegeben, gemischt (alternativ), in einer kontrollierten Reihenfolge, damit wir überprüfen können, ob wir diese stderr / stdout-Reihenfolge trotz der Markierung von stdout beibehalten haben. 2): gnu tail, maybe, but not regulärer Schwanz (ex, aix.). 3): grep "^" zeigt auch beide Dateinamen an. 4): Dies kann durch die Variable geändert werden. 5): Das gewundene Beispiel funktioniert auf alten oses (ex, old aix), wo ich es getestet habe (kein Perl verfügbar).
Olivier Dulac
(1) Mehrfachecho zu stderr und stout wäre in Ordnung, aber okay, nicht wichtig, wäre nur einfacher zu lesen. (3) stimme zu, (4) sicher, aber es wird fehlschlagen, wenn es mit dem beginnt, was die Variable enthält. (5) Ich verstehe.
pLumo
@RoVo Ich bin einverstanden mit Ihrer 1). für 4) könnte die Variable so komplex sein, wie es nötig ist, um das pb verschwinden zu lassen (Beispiel: uniquetag="banaNa11F453355B28E1158D4E516A2D3EDF96B3450406...)
Olivier Dulac
1
Sicher, es ist nicht sehr wahrscheinlich, aber es könnte dennoch zu einem späteren Zeitpunkt ein Sicherheitsproblem auftauchen. Und dann möchten Sie vielleicht diesen String entfernen, bevor Sie in eine Datei
drucken ;-)
2

Wenn die Ausgabereihenfolge lauten muss: stdout then stderr ; Es gibt keine Lösung nur mit Umleitung.
Das stderr muss in einer temporalen Datei gespeichert werden

cmd 2>>file-err | tee -a file1 >>file2
cat file-err >> file1
rm file-err

Beschreibung:

Die einzige Möglichkeit, eine Ausgabe (eine fd wie stdout oder stderr) in zwei Dateien umzuleiten, besteht darin, sie zu reproduzieren. Der Befehl teeist das richtige Werkzeug, um den Inhalt eines Dateideskriptors zu reproduzieren. Eine anfängliche Idee, eine Ausgabe für zwei Dateien zu haben, wäre also, Folgendes zu verwenden:

... |  tee file1 file2

Das gibt den Standardwert von tee für beide Dateien (1 & 2) wieder und lässt die Ausgabe von tee ungenutzt. Aber wir müssen anhängen (benutzen -a) und brauchen nur eine Kopie. Dies löst beide Probleme:

... | tee -a file1 >>file2

Um teestdout (das zu wiederholende) zu liefern, müssen wir stderr direkt aus dem Befehl heraus konsumieren. Eine Möglichkeit, wenn die Reihenfolge nicht wichtig ist (die Reihenfolge der Ausgabe wird (höchstwahrscheinlich) als generiert beibehalten, je nachdem, was zuerst ausgegeben wird, wird zuerst gespeichert). Entweder:

  1. cmd 2>>file1 | tee -a file2 >>file1
  2. cmd 2>>file1 > >( tee -a file2 >>file1 )
  3. ( cmd | tee -a file2 ) >> file1 2>&1

Option 2 funktioniert nur in einigen Shells. Option 3 verwendet eine zusätzliche Unterschale (langsamer), verwendet die Dateinamen jedoch nur einmal.

Aber wenn stdout zuerst sein muss (unabhängig davon, welche Auftragsausgabe generiert wird), müssen wir stderr speichern, um es am Ende an die Datei anzuhängen (erste Lösung veröffentlicht).

Isaac
quelle
1
Oder in Erinnerung sponge(cmd | tee -a out >> out+err) 2>&1 | sponge >> out+err
behalten
1

Im Interesse der Vielfalt:

Wenn Ihr System unterstützt /dev/stderr, dann

(cmd | tee -a /dev/stderr) 2>> file1 >> file2

wird funktionieren. Die Standardausgabe von cmd wird sowohl an die Standardausgabe als auch an die Standardausgabe der Pipeline gesendet. Der Standardfehler der cmdBypässe tee und kommt aus dem Stderr der Pipeline.

So

  • Der Standardwert der Pipeline ist nur der Standardwert von cmdund
  • Der Stderr der Pipeline ist der Stdout und der Stderr des cmd, vermischt.

Es ist dann einfach, diese Streams an die richtigen Dateien zu senden.

Wie bei fast jedem Ansatz wie diesem (einschließlich der Antwort von Stéphane ) file1kann es vorkommen, dass Zeilen aus der Reihenfolge geraten.

Scott
quelle