Leiten Sie stderr und stdout in Bash um

678

Ich möchte sowohl stdout als auch stderr eines Prozesses in eine einzelne Datei umleiten. Wie mache ich das in Bash?

Flybywire
quelle
2
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.)
jvriesem

Antworten:

761

Schauen Sie hier . Sollte sein:

yourcommand &>filename

(leitet beide stdoutund stderrzum Dateinamen um).

dirkgently
quelle
29
Diese Syntax ist laut Bash Hackers Wiki veraltet . Ist es?
Salman von Abbas
20
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_file und druckt es nach stdout.

Marko
quelle
16
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 :

some_command >file.log 2>&1 

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

f3lix
quelle
47
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>&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 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
Sep 23 15: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:

ls -l /proc/self/fd/
ps xf
Quizac
quelle
1
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.

Guðmundur H.
quelle
21

Seltsamerweise funktioniert dies:

yourcommand &> filename

Dies führt jedoch zu einem Syntaxfehler:

yourcommand &>> filename
syntax error near unexpected token `>'

Sie müssen verwenden:

yourcommand 1>> filename 2>&1

quelle
10
&>>scheint auf BASH 4 zu funktionieren:$ echo $BASH_VERSION 4.1.5(1)-release $ (echo to stdout; echo to stderr > /dev/stderr) &>> /dev/null
user272735
15

Kurze Antwort: Command >filename 2>&1oderCommand &>filename


Erläuterung:

Betrachten Sie den folgenden Code, der das Wort "stdout" in stdout und das Wort "stderror" in stderror druckt.

$ (echo "stdout"; echo "stderror" >&2)
stdout
stderror

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).

$ (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 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:

Bash-Dokumentation zur Umleitung , Erläuterung der Dateideskriptortabellen , Einführung in Zeiger

Evan Rosica
quelle
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.
PatS
11
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 ist verwandt: Schreiben von stdOut & stderr in syslog.

Es funktioniert fast, aber nicht von xinted;


quelle
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>&1 1>&2 2>&3 1>>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
bebbo
quelle
Übrigens hat das bei mir nicht funktioniert (Logdatei ist leer). | tee hat keine Wirkung. Stattdessen habe ich es mit stackoverflow.com/questions/692000/… zum Laufen gebracht
Yaroslav Bulatov
4

In Situationen, in denen "Rohrleitungen" erforderlich sind, können Sie Folgendes verwenden:

| &

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 bash-basierten Lösungen können STDOUT und STDERR getrennt weiterleiten (von STDERR von "sort -c" oder von STDERR bis "sort -h").

dmitry_podyachev
quelle
1

"Einfachster" Weg (nur bash4) : ls * 2>&- 1>&-.

reim
quelle
1

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 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"
    }

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"
Fernando Fabreti
quelle
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 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"
thom schumacher
quelle
1

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:

function myfunc(){
  [...]
}

myfunc &>mylog.log
user2761220
quelle
0

Für tcsh muss ich den folgenden Befehl verwenden:

command >& file

Bei Verwendung command &> filewird der Fehler "Ungültiger Nullbefehl" ausgegeben.

einstein6
quelle