Redirect stderr und stdout in Bash
Frage
Ich möchte sowohl stdout und stderr eines Prozesses in einer einzigen Datei umleiten. Wie kann ich tun, dass in Bash?
Lösung
Hier finden Sie aktuelle hier . Sollte sein:
yourcommand &>filename
(Umleitungen sowohl stdout
und stderr
auf Dateinamen).
Andere Tipps
do_something 2>&1 | tee -a some_file
Das wird stderr nach stdout und stdout umleiten some_file
und Druck es auf stdout.
Sie können die Umleitung stderr stdout und stdout in eine Datei:
some_command >file.log 2>&1
Siehe http://tldp.org/LDP/abs/html/io -redirection.html
Dieses Format wird bevorzugt als das beliebteste und> Format, das nur in bash arbeiten. In der Bourne-Shell als Ausführung des Befehls im Hintergrund interpretiert werden könnte. Auch das Format ist lesbarer 2 (STDERR ist) umgeleitet 1 (stdout).
EDIT: änderte sich die Reihenfolge wie in den Kommentaren darauf hingewiesen,
# Close STDOUT file descriptor
exec 1<&-
# Close STDERR FD
exec 2<&-
# Open STDOUT as $LOG_FILE file for read and write.
exec 1<>$LOG_FILE
# Redirect STDERR to STDOUT
exec 2>&1
echo "This line will appear in $LOG_FILE, not 'on screen'"
Nun, einfach Echo auf $ LOG_FILE schreiben. Nützlich für daemonizing.
Um den Autor des ursprünglichen Beitrags,
Es hängt davon ab, was Sie erreichen müssen. Wenn Sie müssen nur umleiten in / aus einem Befehl, den Sie von Ihrem Skript aufrufen, werden die Antworten schon gegeben. Mine ist über Umleitung in aktuelle Skript, das alle Befehle / Einbauten beeinflusst (einschließlich Gabeln) nach dem genannten Code-Schnipsel.
Eine andere kühle Lösung ist sowohl std-err über Umleitung / out und Logger oder Log-Datei einmal, die beinhaltet Splitting „einen Strom“ in zwei Teile. Diese Funktionalität wird von ‚T-Stück‘ Befehl bereitgestellt, die auf mehrere Dateibeschreibungen (Dateien, Steckdosen, Leitungen, usw.) auf einmal schreiben kann / anhängen: tee Datei1 Datei2 ...> (cmd1)> (cmd2) ...
exec 3>&1 4>&2 1> >(tee >(logger -i -t 'my_script_tag') >&3) 2> >(tee >(logger -i -t 'my_script_tag') >&4)
trap 'cleanup' INT QUIT TERM EXIT
get_pids_of_ppid() {
local ppid="$1"
RETVAL=''
local pids=`ps x -o pid,ppid | awk "\\$2 == \\"$ppid\\" { print \\$1 }"`
RETVAL="$pids"
}
# Needed to kill processes running in background
cleanup() {
local current_pid element
local pids=( "$$" )
running_pids=("${pids[@]}")
while :; do
current_pid="${running_pids[0]}"
[ -z "$current_pid" ] && break
running_pids=("${running_pids[@]:1}")
get_pids_of_ppid $current_pid
local new_pids="$RETVAL"
[ -z "$new_pids" ] && continue
for element in $new_pids; do
running_pids+=("$element")
pids=("$element" "${pids[@]}")
done
done
kill ${pids[@]} 2>/dev/null
}
Also, von Anfang an. Nehmen wir an, wir haben Anschluss mit / dev / stdout (FD # 1) und / dev / stderr (FD # 2). In der Praxis könnte es ein Rohr, Buchse oder was auch immer sein.
- Erstellen FDs # 3 und # 4 und auf die gleiche "location" als # 1 und # 2 ist. FD ändern # 1 hat keinen Einfluss auf FD # 3 von jetzt an. Nun FDs # 3 und # 4 Punkt auf STDOUT und STDERR sind. Diese werden als verwendet werden reale Terminal STDOUT und STDERR.
- 1>> (...) leitet STDOUT in Pars zu befehlen
- Pars (Unterschale) ausführt ‚tee‘ Lesen aus STDOUT des exec (pipe) und leitet zu ‚Logger‘ Befehl über eine andere Leitung zur Unterschale im Pars. Zur gleichen Zeit, kopiert es die gleichen Eingang zu FD # 3 (Terminal)
- der zweite Teil, sehr ähnlich ist, ist über den gleichen Trick für STDERR und FDs # 2 und # 4 zu tun.
Das Ergebnis ein Skript läuft die obige Zeile und zusätzlich diese:
echo "Will end up in STDOUT(terminal) and /var/log/messages"
... ist wie folgt:
$ ./my_script
Will end up in STDOUT(terminal) and /var/log/messages
$ tail -n1 /var/log/messages
Sep 23 15:54:03 wks056 my_script_tag[11644]: Will end up in STDOUT(terminal) and /var/log/messages
Wenn Sie möchten, klareres Bild sehen, fügen Sie diese 2 Zeilen zum Skript:
ls -l /proc/self/fd/
ps xf
bash your_script.sh 1>file.log 2>&1
1>file.log
weist die Shell STDOUT an die Datei file.log
zu senden, und es sagt 2>&1
STDERR (Dateideskriptor 2) zu STDOUT (Dateideskriptor 1).
. Hinweis: Die Reihenfolge Angelegenheiten wie liw.fi wies darauf hin, 2>&1 1>file.log
funktioniert nicht
Erstaunlicherweise funktioniert das:
yourcommand &> filename
Aber das gibt einen Syntaxfehler:
yourcommand &>> filename
syntax error near unexpected token `>'
Sie haben zu verwenden:
yourcommand 1>> filename 2>&1
Kurze Antwort: Command >filename 2>&1
oder Command &>filename
Erklärung:
Betrachten Sie den folgenden Code, druckt das Wort „stdout“ zu stdout und das Wort „stderror“ stderror.
$ (echo "stdout"; echo "stderror" >&2)
stdout
stderror
Hinweis , dass der ‚&‘ Operator bash sagt, dass 2 ein Dateideskriptor (die die Stderr Punkt) und keine Dateinamen. Wenn wir nach links, um den ‚&‘, würde dieser Befehl stdout
auf stdout drucken, und erstellen Sie eine Datei namens „2“ und schreibt stderror
dort.
Mit dem oben mit dem Code zu experimentieren, können Sie selbst ganz genau, wie die Umleitung Betreiber der Arbeit. Zum Beispiel, indem Sie die Datei, die von den beiden Deskriptoren 1,2
, um /dev/null
die folgenden zwei Codezeilen löscht alles aus dem stdout und alles von stderror bzw. (Druck was bleibt) wird umgeleitet.
$ (echo "stdout"; echo "stderror" >&2) 1>/dev/null
stderror
$ (echo "stdout"; echo "stderror" >&2) 2>/dev/null
stdout
Jetzt können wir erklären, warum die Lösung, warum der folgende Code keine Ausgabe erzeugt:
(echo "stdout"; echo "stderror" >&2) >/dev/null 2>&1
Um dies wirklich zu verstehen, empfehle ich Ihnen diese Webseite auf Dateideskriptor Tabellen . Angenommen, Sie haben gezeigt, dass das Lesen fertig sind, können wir fortfahren. Beachten Sie, dass Bash Prozesse links nach rechts; so heftiger Schlag sieht >/dev/null
ersten (die die gleiche wie 1>/dev/null
ist), und setzt den Dateideskriptor 1 bis Punkt zu / dev / null anstelle des stdout. dies getan zu haben, Bash bewegt sich dann nach rechts und sieht 2>&1
. Dies stellt den Dateideskriptor 2 auf die gleiche Datei zeigen als Dateideskriptor 1 (und nicht Descriptor 1 selbst in Datei !!!! (siehe diese Ressource auf Zeiger für weitere Informationen )). Da Dateideskriptor 1 Punkte zu / dev / null, und Dateideskriptor 2 zeigen auf die gleiche Datei wie Dateideskriptor 1, Dateideskriptor 2 nun auch auf / dev / null. So weisen beide Filedeskriptoren auf / dev / null, und aus diesem Grund keine Ausgabe gerendert wird.
Um zu testen, ob Sie wirklich das Konzept zu verstehen, versuchen, die Ausgabe zu erraten, wenn wir den Nachsendeauftrag wechseln:
(echo "stdout"; echo "stderror" >&2) 2>&1 >/dev/null
stderror
Die hier Argumentation ist, dass von links nach rechts Auswertung sieht Bash 2> & 1, und setzt damit den Dateideskriptor 2 zu Punkt an der gleichen Stelle wie Dateideskriptor 1, dh stdout. Er setzt dann Dateideskriptor 1 (nicht vergessen, dass> / dev / null = 1> / dev / null) zu> / dev / null-zu-Punkt, also alles zu löschen, die in der Regel an, um den Standard senden würden. So sind wir alle links mit war das, was nicht in der Subshell (der Code in Klammern) an stdout wurde senden - das heißt „stderror“.
Das Interessante daran es zu beachten ist, dass, obwohl 1 nur ein Zeiger auf den stdout, Zeigers 2: 1 über 2>&1
Umleitung nicht bildet eine Kette von Zeigern 2 -> 1 -> stdout. Wenn ja, als Ergebnis der Umleitung 1 auf / dev / null, würde der Code 2>&1 >/dev/null
gibt der Zeigerkette 2 -> 1 -> / dev / null, und somit würde der Code nichts erzeugen, im Gegensatz zu dem, was wir oben gesehen haben .
Schließlich würde ich fest, dass es ein einfacherer Weg, dies zu tun:
$ (echo "stdout"; echo "stderror" >&2) &>/dev/null
SCHLUSSELERKENNTNISSE:
- Datei-Deskriptoren wie Zeiger verhalten (obwohl Filedeskriptoren als Dateizeiger nicht gleich sind)
- einen Dateideskriptor „a“ auf einen Dateideskriptor „B“ Umleiten der „f“ zeigt auf Datei verursacht Dateideskriptor „a“ auf den gleichen plac Punkte als Dateideskriptor b - Datei "f". Es muss nicht eine Kette von Zeigern eines -> b -> f
- Aufgrund der oben genannten, um Angelegenheiten,
2>&1 >/dev/null
ist! =>/dev/null 2>&1
. Ein Ausgang erzeugt und der andere nicht!
Schließlich haben einen Blick auf diesen großen Ressourcen:
Bash-Dokumentation auf Redirection , eine Erklärung von File Descriptor Tables , Einführung in die Pointers
LOG_FACILITY="local7.notice"
LOG_TOPIC="my-prog-name"
LOG_TOPIC_OUT="$LOG_TOPIC-out[$$]"
LOG_TOPIC_ERR="$LOG_TOPIC-err[$$]"
exec 3>&1 > >(tee -a /dev/fd/3 | logger -p "$LOG_FACILITY" -t "$LOG_TOPIC_OUT" )
exec 2> >(logger -p "$LOG_FACILITY" -t "$LOG_TOPIC_ERR" )
Es wird erzählt:. Schreiben StdOut & stderr an syslog
Es ist fast Arbeit, aber nicht von xinted; (
wollte ich eine Lösung, die die Ausgabe von stdout und stderr in eine Protokolldatei und stderr auf der Konsole noch geschrieben haben. Also brauchte ich die stderr-Ausgabe über T-Stück zu duplizieren.
Dies ist die Lösung, die ich gefunden:
command 3>&1 1>&2 2>&3 1>>logfile | tee -a logfile
- Erste Swap stderr und stdout
- dann fügen Sie den stdout in die Protokolldatei
- Rohr Stderr abschlagen und sie auch in die Protokolldatei anhängen
Für Situation, wenn "Rohrleitungen" ist notwendig, um Sie verwenden können:
| &
Zum Beispiel:
echo -ne "15\n100\n"|sort -c |& tee >sort_result.txt
oder
TIMEFORMAT=%R;for i in `seq 1 20` ; do time kubectl get pods |grep node >>js.log ; done |& sort -h
Diese schlag-basierten Lösungen können Rohr STDOUT- und STDERR getrennt (von STDERR von "sort -c" oder von STDERR auf "sort -h").
"einfachste" Weg (bash4 only):. ls * 2>&- 1>&-
Die folgenden Funktionen verwendet werden können, um den Prozess des Hin- und Herschalten Ausgänge beetwen stdout / Stderr und ein Logfile zu automatisieren.
#!/bin/bash
#set -x
# global vars
OUTPUTS_REDIRECTED="false"
LOGFILE=/dev/stdout
# "private" function used by redirect_outputs_to_logfile()
function save_standard_outputs {
if [ "$OUTPUTS_REDIRECTED" == "true" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: Cannot save standard outputs because they have been redirected before"
exit 1;
fi
exec 3>&1
exec 4>&2
trap restore_standard_outputs EXIT
}
# Params: $1 => logfile to write to
function redirect_outputs_to_logfile {
if [ "$OUTPUTS_REDIRECTED" == "true" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: Cannot redirect standard outputs because they have been redirected before"
exit 1;
fi
LOGFILE=$1
if [ -z "$LOGFILE" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: logfile empty [$LOGFILE]"
fi
if [ ! -f $LOGFILE ]; then
touch $LOGFILE
fi
if [ ! -f $LOGFILE ]; then
echo "[ERROR]: ${FUNCNAME[0]}: creating logfile [$LOGFILE]"
exit 1
fi
save_standard_outputs
exec 1>>${LOGFILE%.log}.log
exec 2>&1
OUTPUTS_REDIRECTED="true"
}
# "private" function used by save_standard_outputs()
function restore_standard_outputs {
if [ "$OUTPUTS_REDIRECTED" == "false" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: Cannot restore standard outputs because they have NOT been redirected"
exit 1;
fi
exec 1>&- #closes FD 1 (logfile)
exec 2>&- #closes FD 2 (logfile)
exec 2>&4 #restore stderr
exec 1>&3 #restore stdout
OUTPUTS_REDIRECTED="false"
}
Beispiel für die Verwendung innerhalb Skripts:
echo "this goes to stdout"
redirect_outputs_to_logfile /tmp/one.log
echo "this goes to logfile"
restore_standard_outputs
echo "this goes to stdout"
Für tcsh, ich habe den folgenden Befehl verwenden:
command >& file
Wenn die Verwendung command &> file
, wird es "Invalid null Befehl" Fehler geben.
@ fernando-fabreti
Das Hinzufügen, was Sie habe ich die Funktionen leicht verändert und entfernt, um die & - Schließen und es funktionierte für mich.
function saveStandardOutputs {
if [ "$OUTPUTS_REDIRECTED" == "false" ]; then
exec 3>&1
exec 4>&2
trap restoreStandardOutputs EXIT
else
echo "[ERROR]: ${FUNCNAME[0]}: Cannot save standard outputs because they have been redirected before"
exit 1;
fi
}
# Params: $1 => logfile to write to
function redirectOutputsToLogfile {
if [ "$OUTPUTS_REDIRECTED" == "false" ]; then
LOGFILE=$1
if [ -z "$LOGFILE" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: logfile empty [$LOGFILE]"
fi
if [ ! -f $LOGFILE ]; then
touch $LOGFILE
fi
if [ ! -f $LOGFILE ]; then
echo "[ERROR]: ${FUNCNAME[0]}: creating logfile [$LOGFILE]"
exit 1
fi
saveStandardOutputs
exec 1>>${LOGFILE}
exec 2>&1
OUTPUTS_REDIRECTED="true"
else
echo "[ERROR]: ${FUNCNAME[0]}: Cannot redirect standard outputs because they have been redirected before"
exit 1;
fi
}
function restoreStandardOutputs {
if [ "$OUTPUTS_REDIRECTED" == "true" ]; then
exec 1>&3 #restore stdout
exec 2>&4 #restore stderr
OUTPUTS_REDIRECTED="false"
fi
}
LOGFILE_NAME="tmp/one.log"
OUTPUTS_REDIRECTED="false"
echo "this goes to stdout"
redirectOutputsToLogfile $LOGFILE_NAME
echo "this goes to logfile"
echo "${LOGFILE_NAME}"
restoreStandardOutputs
echo "After restore this goes to stdout"
In Situationen, in denen Sie mit Dingen wie exec 2>&1
betrachten finde ich einfacher zu lesen, wenn möglich, Code Umschreiben bash Funktionen wie folgt aus:
function myfunc(){
[...]
}
myfunc &>mylog.log