wake-up-neo.net

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

Ich kann tee verwenden, um die Ausgabe (STDOUT) von aaa.sh In bbb.out Zu schreiben, während es weiterhin im Terminal angezeigt wird:

./aaa.sh | tee bbb.out

Wie würde ich jetzt auch STDERR in eine Datei mit dem Namen ccc.out Schreiben, während sie noch angezeigt wird?

492
jparanich

Ich gehe davon aus, dass Sie STDERR und STDOUT weiterhin auf dem Terminal sehen möchten. Sie könnten sich für Josh Kelleys Antwort entscheiden, aber ich finde, dass es im Hintergrund ein tail gibt, das Ihre Protokolldatei sehr kitschig und klobig ausgibt. Beachten Sie, wie Sie eine exra-FD behalten und anschließend bereinigen müssen, indem Sie sie töten. Technisch gesehen sollten Sie dies in einem trap '...' EXIT Tun.

Es gibt einen besseren Weg, dies zu tun, und Sie haben es bereits entdeckt: tee.

Nur, anstatt es nur für Ihre stdout zu verwenden, haben Sie ein T-Stück für stdout und eins für stderr. Wie werden Sie das erreichen? Prozessersetzung und Dateiumleitung:

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

Teilen wir es auf und erklären:

> >(..)

>(...) (Prozessersetzung) erstellt ein FIFO und lässt tee darauf hören. Dann verwendet es > (Dateiumleitung) zu Leiten Sie das STDOUT von command zu dem FIFO, das Ihr erstes tee abhört.

Gleiches für die Sekunde:

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

Wir verwenden die Prozessersetzung erneut, um einen tee -Prozess zu erstellen, der aus STDIN liest und in stderr.log Speichert. tee gibt seine Eingabe wieder auf STDOUT aus, aber da seine Eingabe unser STDERR ist, möchten wir das STDOUT von tee wieder auf unser STDERR umleiten. Dann verwenden wir die Dateiumleitung, um STDERR von command zum Eingang des FIFO umzuleiten (STDIN von tee).

Siehe http://mywiki.wooledge.org/BashGuide/InputAndOutput

Prozessersetzung ist eine dieser wirklich schönen Dinge, die Sie als Bonus erhalten, wenn Sie bash als Ihre Shell auswählen, im Gegensatz zu sh (POSIX oder Bourne).


In sh müssten Sie die Dinge manuell erledigen:

out="${TMPDIR:-/tmp}/out.$$" err="${TMPDIR:-/tmp}/err.$$"
mkfifo "$out" "$err"
trap 'rm "$out" "$err"' EXIT
tee -a stdout.log < "$out" &
tee -a stderr.log < "$err" >&2 &
command >"$out" 2>"$err"
725
lhunath

warum nicht einfach:

./aaa.sh 2>&1 | tee -a log

Dies leitet einfach stderr zu stdout um, sodass das Echo sowohl zum Protokollieren als auch zum Anzeigen von Meldungen erfolgt. Vielleicht fehlt mir etwas, weil einige der anderen Lösungen wirklich kompliziert erscheinen.

Anmerkung: Seit der Bash-Version 4 können Sie |& als Abkürzung für 2>&1 |:

./aaa.sh |& tee -a log
621
user542833

Dies kann nützlich sein, wenn Sie dies über Google finden. Deaktivieren Sie einfach das Beispiel, das Sie ausprobieren möchten. Natürlich können Sie die Ausgabedateien auch umbenennen.

#!/bin/bash

STATUSFILE=x.out
LOGFILE=x.log

### All output to screen
### Do nothing, this is the default


### All Output to one file, nothing to the screen
#exec > ${LOGFILE} 2>&1


### All output to one file and all output to the screen
#exec > >(tee ${LOGFILE}) 2>&1


### All output to one file, STDOUT to the screen
#exec > >(tee -a ${LOGFILE}) 2> >(tee -a ${LOGFILE} >/dev/null)


### All output to one file, STDERR to the screen
### Note you need both of these lines for this to work
#exec 3>&1
#exec > >(tee -a ${LOGFILE} >/dev/null) 2> >(tee -a ${LOGFILE} >&3)


### STDOUT to STATUSFILE, stderr to LOGFILE, nothing to the screen
#exec > ${STATUSFILE} 2>${LOGFILE}


### STDOUT to STATUSFILE, stderr to LOGFILE and all output to the screen
#exec > >(tee ${STATUSFILE}) 2> >(tee ${LOGFILE} >&2)


### STDOUT to STATUSFILE and screen, STDERR to LOGFILE
#exec > >(tee ${STATUSFILE}) 2>${LOGFILE}


### STDOUT to STATUSFILE, STDERR to LOGFILE and screen
#exec > ${STATUSFILE} 2> >(tee ${LOGFILE} >&2)


echo "This is a test"
ls -l sdgshgswogswghthb_this_file_will_not_exist_so_we_get_output_to_stderr_aronkjegralhfaff
ls -l ${0}
54
Anthony

Um stderr in eine Datei umzuleiten, zeigen Sie stdout auf dem Bildschirm an und speichern Sie stdout in einer Datei:

./aaa.sh 2> ccc.out | tee ./bbb.out

[~ # ~] edit [~ # ~] : Um sowohl stderr als auch stdout auf dem Bildschirm anzuzeigen und beide in einer Datei zu speichern, können Sie bash verwenden E/A-Umleitung :

#!/bin/bash

# Create a new file descriptor 4, pointed at the file
# which will receive stderr.
exec 4<>ccc.out

# Also print the contents of this file to screen.
tail -f ccc.out &

# Run the command; tee stdout as normal, and send stderr
# to our file descriptor 4.
./aaa.sh 2>&4 | tee bbb.out

# Clean up: Close file descriptor 4 and kill tail -f.
exec 4>&-
kill %1
21
Josh Kelley

Mit anderen Worten, Sie möchten stdout in einen Filter (tee bbb.out) Und stderr in einen anderen Filter (tee ccc.out) Leiten. Es gibt keine Standardmethode, um etwas anderes als stdout in einen anderen Befehl umzuleiten, aber Sie können das umgehen, indem Sie Dateideskriptoren jonglieren.

{ { ./aaa.sh | tee bbb.out; } 2>&1 1>&3 | tee ccc.out; } 3>&1 1>&2

Siehe auch Wie greife ich auf den Standardfehlerstrom (stderr)? und Wann würden Sie einen zusätzlichen Dateideskriptor verwenden?

In bash (und ksh und zsh), aber nicht in anderen POSIX-Shells wie dash, können Sie Prozessersetzung verwenden:

./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out)

Beachten Sie, dass dieser Befehl in bash zurückgegeben wird, sobald ./aaa.sh Beendet ist, auch wenn die Befehle tee noch ausgeführt werden (ksh und zsh warten auf die Unterprozesse). Dies kann ein Problem sein, wenn Sie etwas wie ./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out); process_logs bbb.out ccc.out ausführen. Verwenden Sie in diesem Fall stattdessen File Descriptor Jonglage oder ksh/zsh.

16
Gilles

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

Das Guthaben (das nicht von oben beantwortet wird) finden Sie hier: http://www.cygwin.com/ml/cygwin/2003-06/msg00772.html

13
ChristopheD

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

# create a combined(stdin and stdout) collector
exec 3 <> combined.log

# stream stderr instead of stdout to tee, while draining all stdout 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 Folge von 2>&1 1>&3, Die in unserem Fall den stderr zu stdout und den stdout zum Deskriptor 3. Zu diesem Zeitpunkt sind stderr und stdout noch nicht kombiniert.

Tatsächlich wird das stderr (als stdin) an tee übergeben, wo es sich bei stderr.log Anmeldet und auch zu Deskriptor 3 umleitet.

Und der Deskriptor 3 Zeichnet es die ganze Zeit auf combined.log Auf. Das combined.log Enthält also sowohl stdout als auch stderr.

3
shuva

In meinem Fall hat ein Skript einen Befehl ausgeführt, während sowohl stdout als auch stderr in eine Datei umgeleitet wurden.

cmd > log 2>&1

Ich musste es so aktualisieren, dass bei einem Fehler einige Maßnahmen basierend auf den Fehlermeldungen ergriffen werden. Ich könnte natürlich das dup entfernen 2>&1 und erfassen Sie das stderr aus dem Skript, aber dann werden die Fehlermeldungen nicht als Referenz in die Protokolldatei aufgenommen. Während die akzeptierte Antwort von @lhunath dasselbe tun soll, leitet sie stdout und stderr zu verschiedenen Dateien um, was nicht das ist, was ich will, aber es hat mir geholfen, das genaue zu finden Lösung, die ich brauche:

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

Mit den oben genannten Optionen verfügt log über eine Kopie von stdout und stderr, und ich kann stderr aus meinem Skript erfassen, ohne mich um stdout kümmern zu müssen.

3
haridsv