Ich möchte sagen, dass dies eine überraschend nützliche Frage ist. Viele Menschen wissen nicht, wie sie dies tun sollen, da sie dies nicht häufig tun müssen und es nicht das am besten dokumentierte Verhalten von Bash ist.
Robert Wm Ruedisueli
2
Manchmal ist es nützlich, die Ausgabe (wie üblich) zu sehen UND sie in eine Datei umzuleiten. Siehe die Antwort von Marko unten. (Ich sage dies hier, weil es einfach ist, nur die erste akzeptierte Antwort zu betrachten, wenn dies ausreicht, um ein Problem zu lösen, aber andere Antworten liefern oft nützliche Informationen.)
Laut wiki.bash-hackers.org/scripting/obsolete scheint es in dem Sinne veraltet zu sein, dass es nicht Teil von POSIX ist, aber die Bash-Manpage erwähnt nicht, dass es in naher Zukunft aus Bash entfernt wird. In der Manpage wird eine Präferenz für '&>' gegenüber '> &' angegeben, die ansonsten gleichwertig ist.
Chepner
13
Ich denke, wir sollten &> nicht verwenden, da es nicht in POSIX ist, und gängige Shells wie "dash" unterstützen es nicht.
Sam Watkins
27
Ein zusätzlicher Hinweis: Wenn Sie dies in einem Skript verwenden, stellen Sie sicher, dass es mit #!/bin/bashanstatt beginnt #!/bin/sh, da in bash erforderlich ist.
Tor Klingberg
8
Oder & >> zum Anhängen statt zum Überschreiben.
Alexander Gonchiy
448
do_something 2>&1| tee -a some_file
Das wird stderr nach stdout und stdout umleiten some_fileund druckt es nach stdout.
Unter AIX (ksh) funktioniert Ihre Lösung. Die akzeptierte Antwort do_something &>filenamenicht. +1.
Zurückgehalten am
11
@ Daniel, aber diese Frage ist speziell über Bash
John La Rooy
3
Ich habe eine Ambiguous output redirect.Idee warum?
Alexandre Holden Daly
1
Ich habe ein Ruby-Skript (das ich in keiner Weise ändern möchte), das Fehlermeldungen in Fettdruck rot druckt. Dieses Ruby-Skript wird dann von meinem Bash-Skript aus aufgerufen (das ich ändern kann). Wenn ich das oben genannte verwende, werden die Fehlermeldungen im Klartext abzüglich der Formatierung gedruckt. Gibt es eine Möglichkeit, die Bildschirmformatierung beizubehalten und die Ausgabe (sowohl stdout als auch stderr) auch in einer Datei abzurufen?
Atlantis
8
Beachten Sie, dass dies (standardmäßig) den Nebeneffekt hat, der sich $?nicht mehr auf den Exit-Status von do_something, sondern auf den Exit-Status von bezieht tee.
Flimm
255
Sie können stderr zu stdout und stdout in eine Datei umleiten :
Dieses Format wird gegenüber dem beliebtesten &> Format bevorzugt, das nur in Bash funktioniert. In der Bourne-Shell kann dies so interpretiert werden, dass der Befehl im Hintergrund ausgeführt wird. Auch das Format ist besser lesbar 2 (ist STDERR) umgeleitet auf 1 (STDOUT).
BEARBEITEN: Die Reihenfolge wurde geändert, wie in den Kommentaren angegeben
Dadurch wird stderr zum ursprünglichen stdout umgeleitet, nicht zu der Datei, in die stdout verschoben wird. Setzen Sie '2> & 1' nach '> file.log' und es funktioniert.
1
Was ist der Vorteil dieses Ansatzes gegenüber some_command &> file.log?
Ubermonkey
6
Wenn Sie an eine Datei anhängen möchten, müssen Sie dies folgendermaßen tun: echo "foo" 2> & 1 1 >> bar.txt AFAIK Es gibt keine Möglichkeit, mit &>
SlappyTheFish
9
Argh, sorry, echo "foo" 1 >> bar.txt 2> & 1
SlappyTheFish
11
Ich denke, die Interpretation, dass 2> & 1 stderr nach stdout umleitet, ist falsch; Ich glaube, es ist genauer zu sagen, dass es stderr an den gleichen Ort sendet, an den stdout gerade geht. Daher ist es wichtig, 2> & 1 nach der ersten Umleitung zu platzieren.
JDG
200
# 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'"
Jetzt schreibt einfaches Echo in $ LOG_FILE. Nützlich zum Dämonisieren.
An den Autor des ursprünglichen Beitrags,
Es kommt darauf an, was Sie erreichen müssen. Wenn Sie nur einen Befehl umleiten müssen, den Sie von Ihrem Skript aus aufrufen, sind die Antworten bereits gegeben. Bei mir geht es um die Umleitung innerhalb des aktuellen Skripts, die alle Befehle / integrierten Funktionen (einschließlich Gabeln) nach dem genannten Code-Snippet betrifft.
Eine andere coole Lösung besteht darin, auf std-err / out UND gleichzeitig auf logger oder log file umzuleiten, wobei "ein Stream" in zwei Teile geteilt wird. Diese Funktionalität wird durch den Befehl 'tee' bereitgestellt, mit dem mehrere Dateideskriptoren (Dateien, Sockets, Pipes usw.) gleichzeitig geschrieben / angehängt werden können: tee FILE1 FILE2 ...> (cmd1)> (cmd2) ...
exec 3>&14>&21>>(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"]&&continuefor element in $new_pids;do
running_pids+=("$element")
pids=("$element""${pids[@]}")donedone
kill ${pids[@]}2>/dev/null
}
Also von Anfang an. Nehmen wir an, wir haben ein Terminal mit / dev / stdout (FD # 1) und / dev / stderr (FD # 2) verbunden. In der Praxis kann es sich um ein Rohr, eine Muffe oder was auch immer handeln.
Erstellen Sie die FDs Nr. 3 und Nr. 4 und zeigen Sie auf denselben "Ort" wie Nr. 1 bzw. Nr. 2. Das Ändern von FD # 1 wirkt sich von nun an nicht mehr auf FD # 3 aus. Nun zeigen die FDs Nr. 3 und Nr. 4 auf STDOUT bzw. STDERR. Diese werden als reales Terminal STDOUT und STDERR verwendet.
1 >> (...) leitet STDOUT zum Befehl in parens um
parens (Sub-Shell) führt das 'Tee'-Lesen von STDOUT (Pipe) von exec aus und leitet über eine andere Pipe zum Befehl' logger 'zur Sub-Shell in Parens weiter. Gleichzeitig kopiert es den gleichen Eingang in FD # 3 (Terminal)
Im zweiten Teil, der sehr ähnlich ist, geht es darum, den gleichen Trick für STDERR und FDs Nr. 2 und Nr. 4 auszuführen.
Das Ergebnis der Ausführung eines Skripts mit der obigen Zeile und zusätzlich dieser:
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
Sep2315:54:03 wks056 my_script_tag[11644]:Will end up in STDOUT(terminal) and /var/log/messages
Wenn Sie ein klareres Bild sehen möchten, fügen Sie dem Skript die folgenden 2 Zeilen hinzu:
nur eine Ausnahme. Im ersten Beispiel haben Sie geschrieben: exec 1 <> $ LOG_FILE. Dies führt dazu, dass die ursprüngliche Protokolldatei immer geschrieben wird. Für eine echte Protokollierung ist der bessere Weg: exec 1 >> $ LOG_FILE, da das Protokoll immer angehängt wird.
Znik
4
Das ist wahr, obwohl es von Absichten abhängt. Mein Ansatz ist es, immer eine eindeutige Protokolldatei mit Zeitstempel zu erstellen. Das andere ist anhängen. Beide Möglichkeiten sind "logrotierbar". Ich bevorzuge separate Dateien, die weniger Analyse erfordern, aber wie gesagt, was auch immer Ihr Boot schweben lässt :)
Quizac
1
Ihre zweite Lösung ist informativ, aber was ist mit dem gesamten Bereinigungscode? Es scheint nicht relevant zu sein, und wenn ja, verwirrt es nur ein ansonsten gutes Beispiel. Ich würde auch gerne sehen, dass es leicht überarbeitet wird, damit die FDs 1 und 2 nicht zum Logger umgeleitet werden, sondern 3 und 4, damit alles, was dieses Skript aufruft, 1 und 2 unter der allgemeinen Annahme, dass stdout == 1, weiter manipuliert und stderr == 2, aber mein kurzes Experimentieren legt nahe, dass dies komplexer ist.
JFlo
1
Ich mag es besser mit dem Bereinigungscode. Es könnte ein wenig Ablenkung vom Kernbeispiel sein, aber das Entfernen würde das Beispiel unvollständig machen. Das Netz ist bereits voll von Beispielen ohne Fehlerbehandlung, oder zumindest eine freundliche Anmerkung, dass es noch ungefähr hundert Codezeilen benötigt, um sicher zu sein.
Zoltan K.
1
Ich wollte den Bereinigungscode näher erläutern. Es ist ein Teil des Skripts, der ergo dämonisiert und gegen das HANG-UP-Signal immun wird. 'tee' und 'logger' sind Prozesse, die von derselben PPID erzeugt werden und die HUP-Trap vom Haupt-Bash-Skript erben. Sobald der Hauptprozess stirbt, werden sie von init [1] geerbt. Sie werden keine Zombies (defunc). Der Bereinigungscode stellt sicher, dass alle Hintergrundaufgaben beendet werden, wenn das Hauptskript stirbt. Dies gilt auch für alle anderen Prozesse, die möglicherweise im Hintergrund erstellt wurden und ausgeführt werden.
Quizac
41
bash your_script.sh 1>file.log 2>&1
1>file.logWeist die Shell an, STDOUT an die Datei zu senden file.log, und 2>&1weist sie an, STDERR (Dateideskriptor 2) an STDOUT (Dateideskriptor 1) umzuleiten.
Hinweis: Die Reihenfolge ist wichtig, wie liw.fi hervorhob, 2>&1 1>file.logfunktioniert nicht.
Beachten Sie, dass der Operator '&' bash mitteilt, dass 2 ein Dateideskriptor ist (der auf den stderr zeigt) und kein Dateiname. Wenn wir das '&' stdoutweglassen, wird dieser Befehl in stdout gedruckt und eine Datei mit dem Namen "2" erstellt und stderrordort geschrieben.
Wenn Sie mit dem obigen Code experimentieren, können Sie selbst genau sehen, wie Umleitungsoperatoren funktionieren. Wenn Sie beispielsweise ändern, welche Datei welcher der beiden Deskriptoren 1,2in /dev/nulldie folgenden zwei Codezeilen umgeleitet wird, löschen Sie alles aus dem stdout bzw. alles aus dem stderror (Drucken der verbleibenden).
Um dies wirklich zu verstehen, empfehle ich Ihnen dringend, diese Webseite in Dateideskriptortabellen zu lesen . Angenommen, Sie haben diese Lektüre durchgeführt, können wir fortfahren. Beachten Sie, dass Bash von links nach rechts verarbeitet wird. Daher sieht Bash >/dev/nullzuerst (was dasselbe ist wie 1>/dev/null) und setzt den Dateideskriptor 1 so, dass er auf / dev / null anstelle von stdout zeigt. Nachdem dies geschehen ist, bewegt sich Bash nach rechts und sieht 2>&1. Dadurch wird der Dateideskriptor 2 so eingestellt , dass er auf dieselbe Datei wie der Dateideskriptor 1 verweist (und nicht auf den Dateideskriptor 1 selbst !!!! (siehe diese Ressource zu Zeigern)Für mehr Information)) . Da Dateideskriptor 1 auf / dev / null zeigt und Dateideskriptor 2 auf dieselbe Datei wie Dateideskriptor 1 zeigt, zeigt Dateideskriptor 2 jetzt auch auf / dev / null. Daher zeigen beide Dateideskriptoren auf / dev / null, weshalb keine Ausgabe gerendert wird.
Um zu testen, ob Sie das Konzept wirklich verstehen, versuchen Sie, die Ausgabe zu erraten, wenn wir die Umleitungsreihenfolge ändern:
(echo "stdout"; echo "stderror">&2)2>&1>/dev/null
Standardfehler
Der Grund hierfür ist, dass Bash bei der Auswertung von links nach rechts 2> & 1 sieht und somit den Dateideskriptor 2 so einstellt, dass er auf dieselbe Stelle wie der Dateideskriptor 1 zeigt, dh stdout. Anschließend wird der Dateideskriptor 1 (denken Sie daran, dass> / dev / null = 1> / dev / null) auf> / dev / null verweist, wodurch alles gelöscht wird, was normalerweise an den Standardausgang gesendet wird. Wir haben also nur noch das übrig, was nicht an stdout in der Subshell gesendet wurde (der Code in Klammern) - dh "stderror". Das Interessante dabei ist, dass, obwohl 1 nur ein Zeiger auf das Standardout ist, das Umleiten des Zeigers 2 auf 1 über 2>&1NICHT eine Kette von Zeigern 2 -> 1 -> Standardout bildet. Wenn dies der Fall ist, wird der Code durch Umleiten von 1 nach / dev / null weitergeleitet2>&1 >/dev/null würde der Zeigerkette 2 -> 1 -> / dev / null geben, und somit würde der Code im Gegensatz zu dem, was wir oben gesehen haben, nichts erzeugen.
Abschließend möchte ich darauf hinweisen, dass es einen einfacheren Weg gibt, dies zu tun:
In Abschnitt 3.6.4 sehen wir, dass wir den Operator verwenden können &>, um sowohl stdout als auch stderr umzuleiten. Um also sowohl die stderr- als auch die stdout-Ausgabe eines Befehls an umzuleiten \dev\null(wodurch die Ausgabe gelöscht wird), geben wir einfach
$ command &> /dev/null
oder in meinem Beispiel Folgendes ein:
$ (echo "stdout"; echo "stderror">&2)&>/dev/null
Die zentralen Thesen:
Dateideskriptoren verhalten sich wie Zeiger (obwohl Dateideskriptoren nicht mit Dateizeigern identisch sind)
Das Umleiten eines Dateideskriptors "a" zu einem Dateideskriptor "b", der auf die Datei "f" zeigt, bewirkt, dass der Dateideskriptor "a" auf dieselbe Stelle verweist wie der Dateideskriptor b - Datei "f". Es bildet KEINE Zeigerkette a -> b -> f
Aus den oben genannten Gründen 2>&1 >/dev/nullist die Reihenfolge wichtig ! = >/dev/null 2>&1. Einer erzeugt Ausgabe und der andere nicht!
Schauen Sie sich zum Schluss diese großartigen Ressourcen an:
Dateideskriptoren (0, 1, 2) sind nur Offsets in einer Tabelle. Wenn 2> & 1 verwendet wird, ist der Effekt Slot FD [2] = dup (1). Wo immer FD [1] zeigte, zeigt FD [2] jetzt auf. Wenn Sie FD [1] so ändern, dass es auf / dev / null zeigt, wird FD [1] geändert, aber der FD [2] -Slot (der auf stdout zeigt) wird nicht geändert. Ich verwende den Begriff dup (), weil dies der Systemaufruf ist, mit dem der Dateideskriptor dupliziert wird.
Ich vermute, es funktioniert nicht wegen "/ dev / fd / 3 Berechtigung verweigert". Ein Wechsel zu> & 3 kann hilfreich sein.
Quizac
6
Ich wollte eine Lösung, um die Ausgabe von stdout plus stderr in eine Protokolldatei zu schreiben und stderr noch auf der Konsole zu haben. Also musste ich die stderr-Ausgabe per Tee duplizieren.
Dies ist die Lösung, die ich gefunden habe:
command 3>&11>&22>&31>>logfile | tee -a logfile
Tauschen Sie zuerst stderr und stdout aus
Hängen Sie dann den Standard an die Protokolldatei an
Pipe stderr zum Abschlagen und Anhängen an die Protokolldatei
Die folgenden Funktionen können verwendet werden, um das Umschalten von Ausgaben zwischen stdout / stderr und einer Protokolldatei 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 tofunction 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]"fiif[!-f $LOGFILE ];then
touch $LOGFILE
fiif[!-f $LOGFILE ];then
echo "[ERROR]: ${FUNCNAME[0]}: creating logfile [$LOGFILE]"
exit 1fi
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"}
Anwendungsbeispiel im Skript:
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"
Wenn ich Ihre Funktionen verwende und versucht, Standardausgaben wiederherzustellen, erhalte ich das Echo: Schreibfehler:
Falsche Dateinummer
Damit Ihr Skript funktioniert, musste ich diese Zeilen auskommentieren und die Reihenfolge ändern: #exec 1> & - #closes FD 1 (Protokolldatei) #exec 2> & - #closes FD 2 (Protokolldatei); exec 1> & 3 #restore stdout exec 2> & 4 #restore stderr
thom schumacher
Tut mir leid das zu hören. Ich erhalte keine Fehler, wenn ich in CentOS 7, Bash 4.2.46 ausgeführt werde. Ich habe die Referenz mit Anmerkungen versehen, aus der ich diese Befehle erhalten habe. Es ist: Ref: logan.tw/posts/2016/02/20/open-and-close-files-in-bash
Fernando Fabreti
Ich führe diese Befehle unter AIX aus, deshalb wahrscheinlich. Ich habe einen Beitrag für das Update hinzugefügt, das ich gemacht habe.
thom schumacher
1
@ Fernando-Fabreti
Zusätzlich zu dem, was Sie getan haben, habe ich die Funktionen leicht geändert und das & - Schließen entfernt, und es hat bei mir funktioniert.
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 tofunction redirectOutputsToLogfile {if["$OUTPUTS_REDIRECTED"=="false"];then
LOGFILE=$1
if[-z "$LOGFILE"];then
echo "[ERROR]: ${FUNCNAME[0]}: logfile empty [$LOGFILE]"fiif[!-f $LOGFILE ];then
touch $LOGFILE
fiif[!-f $LOGFILE ];then
echo "[ERROR]: ${FUNCNAME[0]}: creating logfile [$LOGFILE]"
exit 1fi
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 die Verwendung von Dingen in Betracht ziehen, exec 2>&1finde ich es einfacher, Code nach Möglichkeit mit Bash-Funktionen wie diesen neu zu schreiben:
Antworten:
Schauen Sie hier . Sollte sein:
(leitet beide
stdout
undstderr
zum Dateinamen um).quelle
#!/bin/bash
anstatt beginnt#!/bin/sh
, da in bash erforderlich ist.Das wird stderr nach stdout und stdout umleiten
some_file
und druckt es nach stdout.quelle
do_something &>filename
nicht. +1.Ambiguous output redirect.
Idee warum?$?
nicht mehr auf den Exit-Status vondo_something
, sondern auf den Exit-Status von beziehttee
.Sie können stderr zu stdout und stdout in eine Datei umleiten :
Siehe http://tldp.org/LDP/abs/html/io-redirection.html
Dieses Format wird gegenüber dem beliebtesten &> Format bevorzugt, das nur in Bash funktioniert. In der Bourne-Shell kann dies so interpretiert werden, dass der Befehl im Hintergrund ausgeführt wird. Auch das Format ist besser lesbar 2 (ist STDERR) umgeleitet auf 1 (STDOUT).
BEARBEITEN: Die Reihenfolge wurde geändert, wie in den Kommentaren angegeben
quelle
Jetzt schreibt einfaches Echo in $ LOG_FILE. Nützlich zum Dämonisieren.
An den Autor des ursprünglichen Beitrags,
Es kommt darauf an, was Sie erreichen müssen. Wenn Sie nur einen Befehl umleiten müssen, den Sie von Ihrem Skript aus aufrufen, sind die Antworten bereits gegeben. Bei mir geht es um die Umleitung innerhalb des aktuellen Skripts, die alle Befehle / integrierten Funktionen (einschließlich Gabeln) nach dem genannten Code-Snippet betrifft.
Eine andere coole Lösung besteht darin, auf std-err / out UND gleichzeitig auf logger oder log file umzuleiten, wobei "ein Stream" in zwei Teile geteilt wird. Diese Funktionalität wird durch den Befehl 'tee' bereitgestellt, mit dem mehrere Dateideskriptoren (Dateien, Sockets, Pipes usw.) gleichzeitig geschrieben / angehängt werden können: tee FILE1 FILE2 ...> (cmd1)> (cmd2) ...
Also von Anfang an. Nehmen wir an, wir haben ein Terminal mit / dev / stdout (FD # 1) und / dev / stderr (FD # 2) verbunden. In der Praxis kann es sich um ein Rohr, eine Muffe oder was auch immer handeln.
Das Ergebnis der Ausführung eines Skripts mit der obigen Zeile und zusätzlich dieser:
...ist wie folgt:
Wenn Sie ein klareres Bild sehen möchten, fügen Sie dem Skript die folgenden 2 Zeilen hinzu:
quelle
1>file.log
Weist die Shell an, STDOUT an die Datei zu sendenfile.log
, und2>&1
weist sie an, STDERR (Dateideskriptor 2) an STDOUT (Dateideskriptor 1) umzuleiten.Hinweis: Die Reihenfolge ist wichtig, wie liw.fi hervorhob,
2>&1 1>file.log
funktioniert nicht.quelle
Seltsamerweise funktioniert dies:
Dies führt jedoch zu einem Syntaxfehler:
Sie müssen verwenden:
quelle
&>>
scheint auf BASH 4 zu funktionieren:$ echo $BASH_VERSION 4.1.5(1)-release $ (echo to stdout; echo to stderr > /dev/stderr) &>> /dev/null
Kurze Antwort:
Command >filename 2>&1
oderCommand &>filename
Erläuterung:
Betrachten Sie den folgenden Code, der das Wort "stdout" in stdout und das Wort "stderror" in stderror druckt.
Beachten Sie, dass der Operator '&' bash mitteilt, dass 2 ein Dateideskriptor ist (der auf den stderr zeigt) und kein Dateiname. Wenn wir das '&'
stdout
weglassen, wird dieser Befehl in stdout gedruckt und eine Datei mit dem Namen "2" erstellt undstderror
dort geschrieben.Wenn Sie mit dem obigen Code experimentieren, können Sie selbst genau sehen, wie Umleitungsoperatoren funktionieren. Wenn Sie beispielsweise ändern, welche Datei welcher der beiden Deskriptoren
1,2
in/dev/null
die folgenden zwei Codezeilen umgeleitet wird, löschen Sie alles aus dem stdout bzw. alles aus dem stderror (Drucken der verbleibenden).Jetzt können wir erklären, warum die Lösung, warum der folgende Code keine Ausgabe erzeugt:
Um dies wirklich zu verstehen, empfehle ich Ihnen dringend, diese Webseite in Dateideskriptortabellen zu lesen . Angenommen, Sie haben diese Lektüre durchgeführt, können wir fortfahren. Beachten Sie, dass Bash von links nach rechts verarbeitet wird. Daher sieht Bash
>/dev/null
zuerst (was dasselbe ist wie1>/dev/null
) und setzt den Dateideskriptor 1 so, dass er auf / dev / null anstelle von stdout zeigt. Nachdem dies geschehen ist, bewegt sich Bash nach rechts und sieht2>&1
. Dadurch wird der Dateideskriptor 2 so eingestellt , dass er auf dieselbe Datei wie der Dateideskriptor 1 verweist (und nicht auf den Dateideskriptor 1 selbst !!!! (siehe diese Ressource zu Zeigern)Für mehr Information)) . Da Dateideskriptor 1 auf / dev / null zeigt und Dateideskriptor 2 auf dieselbe Datei wie Dateideskriptor 1 zeigt, zeigt Dateideskriptor 2 jetzt auch auf / dev / null. Daher zeigen beide Dateideskriptoren auf / dev / null, weshalb keine Ausgabe gerendert wird.Um zu testen, ob Sie das Konzept wirklich verstehen, versuchen Sie, die Ausgabe zu erraten, wenn wir die Umleitungsreihenfolge ändern:
Der Grund hierfür ist, dass Bash bei der Auswertung von links nach rechts 2> & 1 sieht und somit den Dateideskriptor 2 so einstellt, dass er auf dieselbe Stelle wie der Dateideskriptor 1 zeigt, dh stdout. Anschließend wird der Dateideskriptor 1 (denken Sie daran, dass> / dev / null = 1> / dev / null) auf> / dev / null verweist, wodurch alles gelöscht wird, was normalerweise an den Standardausgang gesendet wird. Wir haben also nur noch das übrig, was nicht an stdout in der Subshell gesendet wurde (der Code in Klammern) - dh "stderror". Das Interessante dabei ist, dass, obwohl 1 nur ein Zeiger auf das Standardout ist, das Umleiten des Zeigers 2 auf 1 über
2>&1
NICHT eine Kette von Zeigern 2 -> 1 -> Standardout bildet. Wenn dies der Fall ist, wird der Code durch Umleiten von 1 nach / dev / null weitergeleitet2>&1 >/dev/null
würde der Zeigerkette 2 -> 1 -> / dev / null geben, und somit würde der Code im Gegensatz zu dem, was wir oben gesehen haben, nichts erzeugen.Abschließend möchte ich darauf hinweisen, dass es einen einfacheren Weg gibt, dies zu tun:
In Abschnitt 3.6.4 sehen wir, dass wir den Operator verwenden können
&>
, um sowohl stdout als auch stderr umzuleiten. Um also sowohl die stderr- als auch die stdout-Ausgabe eines Befehls an umzuleiten\dev\null
(wodurch die Ausgabe gelöscht wird), geben wir einfach$ command &> /dev/null
oder in meinem Beispiel Folgendes ein:Die zentralen Thesen:
2>&1 >/dev/null
ist die Reihenfolge wichtig ! =>/dev/null 2>&1
. Einer erzeugt Ausgabe und der andere nicht!Schauen Sie sich zum Schluss diese großartigen Ressourcen an:
Bash-Dokumentation zur Umleitung , Erläuterung der Dateideskriptortabellen , Einführung in Zeiger
quelle
Es ist verwandt: Schreiben von stdOut & stderr in syslog.
Es funktioniert fast, aber nicht von xinted;
quelle
Ich wollte eine Lösung, um die Ausgabe von stdout plus stderr in eine Protokolldatei zu schreiben und stderr noch auf der Konsole zu haben. Also musste ich die stderr-Ausgabe per Tee duplizieren.
Dies ist die Lösung, die ich gefunden habe:
quelle
In Situationen, in denen "Rohrleitungen" erforderlich sind, können Sie Folgendes verwenden:
Zum Beispiel:
oder
Diese bash-basierten Lösungen können STDOUT und STDERR getrennt weiterleiten (von STDERR von "sort -c" oder von STDERR bis "sort -h").
quelle
"Einfachster" Weg (nur bash4) :
ls * 2>&- 1>&-
.quelle
Die folgenden Funktionen können verwendet werden, um das Umschalten von Ausgaben zwischen stdout / stderr und einer Protokolldatei zu automatisieren.
Anwendungsbeispiel im Skript:
quelle
@ Fernando-Fabreti
Zusätzlich zu dem, was Sie getan haben, habe ich die Funktionen leicht geändert und das & - Schließen entfernt, und es hat bei mir funktioniert.
quelle
In Situationen, in denen Sie die Verwendung von Dingen in Betracht ziehen,
exec 2>&1
finde ich es einfacher, Code nach Möglichkeit mit Bash-Funktionen wie diesen neu zu schreiben:quelle
Für tcsh muss ich den folgenden Befehl verwenden:
Bei Verwendung
command &> file
wird der Fehler "Ungültiger Nullbefehl" ausgegeben.quelle