Kopieren Sie stdout und stderr in eine Protokolldatei und lassen Sie sie auf der Konsole im Skript selbst

11

Wie kopiere ich mit bash stderr und stdout in eine Protokolldatei und lasse sie auch auf der Konsole angezeigt?

Ich möchte dies innerhalb des Skripts selbst mit einem exec tun.

Ich habe es mit versucht

exec &>> log.out

echo "This is stdout"
echo "This is stderr" >&2

Aber das obige druckt nichts auf der Konsole. Wie kann ich dies in Bash erreichen?

adarshr
quelle
Auf StackOverflow gibt es eine sehr positive Antwort auf eine ähnliche Frage, die diese Frage ziemlich gründlich beantwortet. stackoverflow.com/a/692407/208257
Dan Burton

Antworten:

9

Sie suchen tee.

Siehe man teefür Details.

Um es mit zu kombinieren exec, müssen Sie die Prozessersetzung verwenden . (Siehe man bashfür Details.)

exec &> >(tee  log.out)
echo "This is stdout"
echo "This is stderr" >&2
H.-Dirk Schmitt
quelle
Ich habe es angeschaut. Es werden exec 2>&1 | tee -a log.outnur Drucke auf der Konsole ausgeführt, nichts in der Protokolldatei. Vielleicht verwende ich nicht die richtige Syntax?
Adarshr
@adarshr Bitte überprüfen Sie Ihren Code. Ich habe teein Kombination mit Prozesssubstitution darüber geschrieben .
H.-Dirk Schmitt
1
Das verschmilzt jedoch stdout und stderr und kann die gleiche Art von Nebenwirkung haben, wie in meiner Antwort erwähnt.
Stéphane Chazelas
@StephaneChazelas Sehen Sie sich den Beispielcode in der Frage an - das war die Absicht des OP.
H.-Dirk Schmitt
Was ich unter Zusammenführen verstehe, ist, dass jetzt die Fehler des Skripts auf stdout gehen. Wenn dies the-script | wc -lzum Beispiel jemand tut , zählt dies auch die Fehlerzeilen und Sie werden die Fehler nicht sehen.
Stéphane Chazelas
5

Du kannst tun:

: > log # empty log file if necessary
{ { {

  ...the script

} 3>&- | tee -a log >&3 3>&-
exit "${PIPESTATUS[0]}"
} 2>&1 | tee -a log >&2 3>&-
} 3>&1
exit "${PIPESTATUS[0]}"

Sie könnten es auch schreiben als:

: > log # empty log file if necessary
exec 2> >(tee -a log >&2) > >(tee -a log)

...the script

Da bash jedoch nicht auf die Prozesse wartet, mit denen begonnen >(...wurde, hat dies den unangenehmen Effekt, dass manchmal etwas an das Terminal ausgegeben wird, nachdem der Befehl zurückgegeben wurde, was noch schlimmere Auswirkungen haben kann (z. B. das stille Verwerfen dieser Ausgabe), wenn das Terminal-Attribut "tostop" ist an.

stdoutWenn Sie in beiden Lösungen eine Pipe erstellen und zwei Befehle unabhängig voneinander die Ausgabe- und Fehlermeldungen ausgeben, wirkt sich dies in jedem Fall auf die Ausgabepufferung und die Reihenfolge aus, in der die Ausgabe- und Fehlermeldungen angezeigt werden.

Stéphane Chazelas
quelle
Ich denke, Ihrem zweiten Tee fehlt eine Weiterleitung zu stderr. Es sollte sein:tee -a log >&2 3>&-
richvdh
5

Ich weiß, dass dies ein alter Beitrag ist, aber warum nicht einfach das tun?

echo "hi" >> log.txt #stdout -> log
echo "hi" | tee -a log.txt #stdout -> log & stdout
echo "hi" &>> log.txt #stdout & stderr -> log
echo "hi" |& tee -a log.txt #stdout & stderr -> log & stdout

Und wenn Sie Standard haben möchten, können Sie natürlich auch regelmäßig drucken.

Sie können dies mit einer beliebigen Kombination von Streams tun, indem Sie nur diese beiden grundlegenden Befehle verwenden.

Ich weiß, dass ich hierher gekommen bin und keine leicht verständliche Antwort erhalten habe. Hoffentlich hilft dies jemand anderem, der Probleme hat.

Übrigens, für Noobs da draußen wie mein vorheriges Ich gibt der teeBefehl lediglich die stdin-Eingabe sowohl an stdout als auch an die als nachfolgende Argumente angegebenen Dateien aus. -asteht für append, damit Sie die Datei nicht bei jeder Verwendung des Befehls überschreiben. Wenn Sie weitere Fragen haben, ist dies eine sehr hilfreiche Ressource, um schnell Bash zu lernen.

user2624583
quelle
1
Vielen Dank für Ihren Beitrag. Ich dagegen habe völlig vergessen, was ich versucht habe :-)
adarshr
1
+1 für mehrere, nette und einfache Abschlagbeispiele.
Tim
2

Eine weitere Möglichkeit besteht darin, Umleitungen innerhalb von Funktionen zu verwenden.

#!/bin/bash

function1 () {
    echo 'STDOUT from function 1'
    echo 'STDERR from function 1' >&2
}

function2 () {
    echo 'STDOUT from function 2'
    echo 'STDERR from function 2' >&2
}


function3 () {
    echo 'STDOUT from function 3'
    echo 'STDERR from function 3' >&2
}

main() {
    function1
    function2
    function3
}

main 2>&1 |tee log.txt

Hier haben wir eine mainFunktion, die alle anderen Funktionen aufruft. Jetzt umleiten STDOUTund STDERRvon mainFunktion zu tee.

Kannan Mohan
quelle
IMHO ist dies der sauberste Weg, dies zu tun, zumindest für einfache Fälle. Vielen Dank.
ACK_stoverflow