Leiten Sie den Befehl stderr aller nachfolgenden Befehle mit exec um

43

Ich habe eine Bash-Datei, die ich alle Ausgaben in eine Datei, Debug-Protokoll sowie auf das Terminal umleiten muss. Ich muss sowohl stdout als auch stderr zum Debug umleiten und es für alle Befehle im Skript protokollieren.

Ich möchte nicht 2>&1 | tee -a $DEBUGfür jeden einzelnen Befehl in der Datei hinzufügen . Ich könnte damit leben | tee -a $DEBUG.

Ich erinnere mich, dass es einen Weg gab, dies mit so etwas wie zu tun exec 2>&1.

Momentan verwende ich etwas wie das Folgende:

#!/bin/bash
DEBUGLOG=/tmp/debug
exec 2>&1
somecommand | tee -a $DEBUGLOG
somecommand2 | tee -a $DEBUGLOG
somecommand3 | tee -a $DEBUGLOG

aber es funktioniert nicht. Hat jemand eine Lösung / kann die Ursache erklären?

Avi
quelle
1
In einigen Shells, |&die als Abkürzung für funktionieren 2>&1 |, ist es zumindest etwas praktischer.
Kevin

Antworten:

39

Eine Lösung, um viele Befehle auf einmal umzuleiten:

#!/bin/bash
{
    somecommand 
    somecommand2
    somecommand3
} 2>&1 | tee -a $DEBUGLOG

Warum Ihre ursprüngliche Lösung nicht funktioniert: exec 2> & 1 leitet die Standardfehlerausgabe an die Standardausgabe Ihrer Shell weiter, die, wenn Sie Ihr Skript von der Konsole aus ausführen, Ihre Konsole ist. Die Pipe-Umleitung bei Befehlen leitet nur die Standardausgabe des Befehls um.

Unter dem Gesichtspunkt von somecommandwird die Standardausgabe in eine Pipe geschrieben, die mit verbunden ist, teeund der Standardfehler wird in dieselbe Datei / Pseudodatei geschrieben wie der Standardfehler der Shell, die Sie zur Standardausgabe der Shell umleiten Konsole, wenn Sie Ihr Programm von der Konsole aus ausführen.

Der einzig wahre Weg, dies zu erklären, ist zu sehen, was wirklich passiert:

Die ursprüngliche Umgebung Ihrer Shell könnte folgendermaßen aussehen, wenn Sie sie vom Terminal aus ausführen:

stdin -> /dev/pts/42
stdout -> /dev/pts/42
stderr -> /dev/pts/42

Nachdem Sie den Standardfehler in die Standardausgabe ( exec 2>&1) umgeleitet haben , ... ändern Sie im Grunde nichts. Wenn Sie jedoch die Standardausgabe des Skripts in eine Datei umleiten, erhalten Sie eine Umgebung wie die folgende:

stdin -> /dev/pts/42
stdout -> /your/file
stderr -> /dev/pts/42

Das Umleiten des Shell-Standardfehlers in die Standardausgabe würde dann folgendermaßen enden:

stdin -> /dev/pts/42
stdout -> /your/file
stderr -> /your/file

Das Ausführen eines Befehls erbt diese Umgebung. Wenn Sie einen Befehl ausführen und ihn an tee weiterleiten, sieht die Umgebung des Befehls folgendermaßen aus:

stdin -> /dev/pts/42
stdout -> pipe:[4242]
stderr -> /your/file

Der Standardfehler Ihres Befehls geht also immer noch in das ein, was die Shell als Standardfehler verwendet.

Sie können die Umgebung eines Befehls tatsächlich anzeigen, indem Sie nachsehen /proc/[pid]/fd: Verwenden Sie ls -ldiese Option, um auch den Inhalt des symbolischen Links aufzulisten. Die 0Datei hier ist Standardeingabe, 1Standardausgabe und 2Standardfehler. Wenn der Befehl mehr Dateien öffnet (und die meisten Programme dies tun), werden sie auch angezeigt. Ein Programm kann auch wählen , um ihre Standard - Ein- / Ausgabe und Wiederverwendung zu umleiten oder zu schließen 0, 1und 2.

BatchyX
quelle
41

Sie können exec wie folgt am oberen Rand Ihres Skripts verwenden:

exec > >(tee "$HOME/somefile.log") 2>&1

Zum Beispiel:

#!/bin/bash -

exec > >(tee "$HOME/somefile.log") 2>&1

echo "$HOME"
echo hi
date
date +%F
echo bye 1>&2

Gibt mir eine Ausgabe in die Datei $HOME/somefile.logund in das Terminal wie folgt aus:

/home/saml
hi
Sun Jan 20 13:54:17 EST 2013
2013-01-20
bye
slm
quelle
2
Beachten Sie, dass dies Bashismen verwendet - es funktioniert möglicherweise nicht in anderen Muscheln (z. B. Bindestrich). Aber da die Frage bash spezifiziert, +1.
Richard Hansen
8
@RichardHansen, die Prozessersetzung wurde von ksh eingeführt, nicht von bash, und wird auch von zsh unterstützt, sodass ich es nicht als Bashismus bezeichnen würde .
Stéphane Chazelas
6
@StephaneChazelas: Du machst einen guten Punkt. Ich wollte nur darauf hinweisen, dass die Syntax vom POSIX-Standard nicht unterstützt wird und daher in /bin/shSkripten nicht universell funktioniert (viele Leute verwenden fälschlicherweise die Bash-Syntax in /bin/shSkripten).
Richard Hansen
Für mich gibt dies /dev/fd/62: Operation not supportedkeine Hinweise?
Eun
1
Gibt es eine Möglichkeit, stderr nur in die Protokolldatei umzuleiten? Wenn das Originalskript ist myscriptund ich laufe ./myscript > /dev/null, sollte ich immer noch sehen, byewas kommt echo bye >&2.
Martin Jambon
0

Schreibe stderr und stdout in eine Datei, zeige stderr auf dem Bildschirm an (auf stdout)

exec 2> >(tee -a -i "$HOME/somefile.log")
exec >> "$HOME/somefile.log"

Nützlich für Benutzer, damit Sie Fehler (und nur Fehler) per E-Mail erhalten können

Lluís
quelle