Frage

Ich möchte sowohl stdout und stderr eines Prozesses in einer einzigen Datei umleiten. Wie kann ich tun, dass in Bash?

War es hilfreich?

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

umleiten

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

Aus dem Bereich 3.6.4 rel="noreferrer"> sehen wir, dass wir können die Betreiber &> verwenden sowohl stdout und stderr zu umleiten. Somit zu umleiten sowohl die Stderr und stdout Ausgabe jeden Befehl \dev\null (die den Ausgang löscht), geben wir einfach $ command &> /dev/null oder bei meinem Beispiel:

$ (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
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top