780 Stimmen

Wie schreibe ich einen Standardfehler in eine Datei, während ich "tee" mit einer Pipe verwende?

Ich weiß, wie man tee um die Ausgabe zu schreiben ( Standardausgabe ) von aaa.sh a bbb.out und wird dennoch im Terminal angezeigt:

./aaa.sh | tee bbb.out

Wie würde ich jetzt auch schreiben Standardfehler in eine Datei namens ccc.out und trotzdem angezeigt wird?

17voto

ChristopheD Punkte 106139

Wenn Sie Bash verwenden:

# Redirect standard out and standard error separately
% cmd >stdout-redirect 2>stderr-redirect

# Redirect standard error and out together
% cmd >stdout-redirect 2>&1

# Merge standard error with standard out and pipe
% cmd 2>&1 |cmd2

Der Vermerk (ich antworte nicht aus dem Stegreif) findet sich hier: Re: bash: stderr & mehr (pipe für stderr)

13voto

Arminius Punkte 1821

Wenn Sie Folgendes verwenden Z-Muschel ( zsh ), können Sie mehrere Umleitungen verwenden, so dass Sie nicht einmal tee :

./cmd 1>&1 2>&2 1>out_file 2>err_file

Hier leiten Sie einfach jeden Stream auf sich selbst um et die Zieldatei.


Vollständiges Beispiel

% (echo "out"; echo "err">/dev/stderr) 1>&1 2>&2 1>/tmp/out_file 2>/tmp/err_file
out
err
% cat /tmp/out_file
out
% cat /tmp/err_file
err

Beachten Sie, dass dies die MULTIOS gesetzt werden (dies ist die Standardeinstellung).

MULTIOS

Implizit ausführen tee s oder cat s, wenn mehrere Umleitungen versucht werden (siehe Umleitung ).

11voto

B Jam Punkte 21

Wie die akzeptierte Antwort gut erklärt von lhunath können Sie verwenden

command > >(tee -a stdout.log) 2> >(tee -a stderr.log >&2)

Wenn Sie die Bash verwenden, könnten Sie ein Problem bekommen .

Lassen Sie mich die matthew-wilcoxson Beispiel.

Und für diejenigen, die "sehen, was sie glauben", ein kurzer Test:

(echo "Test Out";>&2 echo "Test Err") > >(tee stdout.log) 2> >(tee stderr.log >&2)

Wenn ich es persönlich versuche, habe ich dieses Ergebnis:

user@computer:~$ (echo "Test Out";>&2 echo "Test Err") > >(tee stdout.log) 2> >(tee stderr.log >&2)
user@computer:~$ Test Out
Test Err

Beide Meldungen erscheinen nicht auf der gleichen Ebene. Warum wird Test Out scheinen, wie wenn es mein vorheriger Befehl wäre?

Die Eingabeaufforderung steht in einer leeren Zeile, was mich glauben lässt, dass der Vorgang noch nicht abgeschlossen ist, und wenn ich auf Enter das bringt es in Ordnung. Wenn ich den Inhalt der Dateien überprüfe, ist er in Ordnung, und die Umleitung funktioniert.

Machen wir einen weiteren Test.

function outerr() {
  echo "out"     # stdout
  echo >&2 "err" # stderr
}

user@computer:~$ outerr
out
err

user@computer:~$ outerr >/dev/null
err

user@computer:~$ outerr 2>/dev/null
out

Versuchen Sie erneut die Umleitung, aber mit dieser Funktion:

function test_redirect() {
  fout="stdout.log"
  ferr="stderr.log"
  echo "$ outerr"
  (outerr) > >(tee "$fout") 2> >(tee "$ferr" >&2)
  echo "# $fout content: "
  cat "$fout"
  echo "# $ferr content: "
  cat "$ferr"
}

Persönlich habe ich dieses Ergebnis:

user@computer:~$ test_redirect
$ outerr
# stdout.log content:
out
out
err
# stderr.log content:
err
user@computer:~$

Keine Eingabeaufforderung in einer leeren Zeile, aber ich sehe keine normale Ausgabe. Die stdout.log Inhalt scheinen falsch zu sein, und nur stderr.log scheint in Ordnung zu sein.

Wenn ich es neu starte, kann die Ausgabe unterschiedlich sein...

Und warum?

Weil, wie hier erklärt :

Beachten Sie, dass dieser Befehl in der Bash zurückkehrt, sobald [erster Befehl] beendet ist, auch wenn die Tee-Befehle noch ausgeführt werden (ksh und zsh warten auf die Unterprozesse)

Wenn Sie also Bash benutzen, verwenden Sie lieber das bessere Beispiel aus diese andere Antwort :

{ { outerr | tee "$fout"; } 2>&1 1>&3 | tee "$ferr"; } 3>&1 1>&2

Damit werden die bisherigen Probleme behoben.

Die Frage ist nun, wie man den Exit-Status-Code abrufen kann.

$? funktioniert nicht.

Ich habe keine bessere Lösung gefunden, als Pipefail einzuschalten mit set -o pipefail ( set +o pipefail zum Ausschalten) und verwenden Sie ${PIPESTATUS[0]} wie diese:

function outerr() {
  echo "out"
  echo >&2 "err"
  return 11
}

function test_outerr() {
  local - # To preserve set option
  ! [[ -o pipefail ]] && set -o pipefail; # Or use second part directly
  local fout="stdout.log"
  local ferr="stderr.log"
  echo "$ outerr"
  { { outerr | tee "$fout"; } 2>&1 1>&3 | tee "$ferr"; } 3>&1 1>&2
  # First save the status or it will be lost
  local status="${PIPESTATUS[0]}" # Save first, the second is 0, perhaps tee status code.
  echo "==="
  echo "# $fout content :"
  echo "<==="
  cat "$fout"
  echo "===>"
  echo "# $ferr content :"
  echo "<==="
  cat "$ferr"
  echo "===>"
  if (( status > 0 )); then
    echo "Fail $status > 0"
    return "$status" # or whatever
  fi
}

user@computer:~$ test_outerr
$ outerr
err
out
===
# stdout.log content:
<===
out
===>
# stderr.log content:
<===
err
===>
Fail 11 > 0

7voto

KRoy Punkte 1122

Das Folgende funktioniert für KornShell (ksh), wo die Prozesssubstitution nicht verfügbar ist,

# create a combined (standard input and standard output) collector
exec 3 <> combined.log

# stream standard error instead of standard output to tee, while draining all standard output to the collector
./aaa.sh 2>&1 1>&3 | tee -a stderr.log 1>&3

# cleanup collector
exec 3>&-

Der eigentliche Trick dabei ist die Reihenfolge der 2>&1 1>&3 die in unserem Fall den Standardfehler auf die Standardausgabe umleitet und die Standardausgabe auf Dateideskriptor 3. Zu diesem Zeitpunkt sind der Standardfehler und die Standardausgabe noch nicht kombiniert.

In der Tat wird der Standardfehler (als Standardeingabe) an tee wo es sich anmeldet stderr.log und leitet auch auf den Dateideskriptor 3 um.

Und der Dateideskriptor 3 protokolliert es in combined.log Die ganze Zeit über. Also die combined.log enthält sowohl die Standardausgabe als auch den Standardfehler.

5voto

haridsv Punkte 8019

In meinem Fall führte ein Skript einen Befehl aus und leitete sowohl stdout als auch stderr in eine Datei um, etwa so:

cmd > log 2>&1

Ich musste sie so aktualisieren, dass sie im Falle eines Fehlers einige Maßnahmen auf der Grundlage der Fehlermeldungen ergreift. Ich könnte natürlich das Dup entfernen 2>&1 und die stderr des Skripts aufzeichnen, aber dann werden die Fehlermeldungen nicht in die Protokolldatei als Referenz aufgenommen. Während die akzeptierten Antwort von lhunath soll dasselbe tun, es leitet um stdout y stderr zu verschiedenen Dateien, was nicht das ist, was ich will, aber es hat mir geholfen, genau die Lösung zu finden, die ich brauche:

(cmd 2> >(tee /dev/stderr)) > log

Auf diese Weise erhält das Protokoll eine Kopie von beiden stdout y stderr und ich kann erfassen stderr von meinem Skript aus, ohne dass ich mich um stdout .

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X