Ich weiß, wie man stdout in eine Datei umleitet :
exec > foo.log
echo test
Dadurch wird der 'Test' in die Datei foo.log eingefügt.
Jetzt möchte ich die Ausgabe in die Protokolldatei umleiten UND auf stdout belassen
dh es kann trivial von außerhalb des Skripts durchgeführt werden:
script | tee foo.log
aber ich möchte es im Skript selbst deklarieren
Ich habe es versucht
exec | tee foo.log
aber es hat nicht funktioniert.
Antworten:
Beachten Sie, dass dies
bash
nicht der Fall istsh
. Wenn Sie das Skript mit aufrufensh myscript.sh
, wird ein Fehler in der Art von angezeigtsyntax error near unexpected token '>'
.Wenn Sie mit Signalfallen arbeiten, möchten Sie möglicherweise die
tee -i
Option verwenden, um eine Unterbrechung des Ausgangs zu vermeiden, wenn ein Signal auftritt. (Danke an JamesThomasMoon1979 für den Kommentar.)Werkzeuge, die ihre Ausgabe ändern, je nachdem, ob sie in eine Pipe oder ein Terminal schreiben (
ls
z. B. mithilfe von Farben und Spaltenausgabe), erkennen das obige Konstrukt so, dass sie in eine Pipe ausgeben.Es gibt Optionen zum Erzwingen des Kolorierens / Kolumnierens (z
ls -C --color=always
. B. ). Beachten Sie, dass dies dazu führt, dass die Farbcodes ebenfalls in die Protokolldatei geschrieben werden, wodurch diese weniger lesbar wird.quelle
tee
die Ausgabe nicht gepuffert werden soll. Wenn es auf den meisten Systemen puffert, ist es auf den meisten Systemen defekt. Das ist ein Problem dertee
Implementierungen, nicht meiner Lösung.exec
ist sehr mächtig, aber auch sehr engagiert. Sie können das aktuelle Standardout auf einem anderen Dateiskriptor "sichern" und später wiederherstellen. Google "Bash Exec Tutorial", es gibt viele fortgeschrittene Sachen da draußen.exec
ist dokumentiert, um keine neuen Prozesse zu starten,>(tee ...)
ist ein Standard namens Pipe / Process Substitution, und das&
in der Umleitung hat natürlich nichts mit Hintergrund zu tun ...? :-)-i
zu gehentee
. Andernfalls stören Signalinterrupts (Traps) stdout im Hauptskript. Wenn Sie beispielsweise eine habentrap 'echo foo' EXIT
und dann drückenctrl+c
, wird " foo " nicht angezeigt . Also würde ich die Antwort auf ändernexec &> >(tee -ia file)
.Die akzeptierte Antwort behält STDERR nicht als separaten Dateideskriptor bei. Das bedeutet
wird nicht
bar
an das Terminal ausgegeben , sondern nur an die Protokolldatei, undgibt beide
foo
undbar
an das Terminal aus. Dies ist eindeutig nicht das Verhalten, das ein normaler Benutzer wahrscheinlich erwartet. Dies kann behoben werden, indem zwei separate Tee-Prozesse verwendet werden, die beide an dieselbe Protokolldatei angehängt werden:(Beachten Sie, dass das oben Gesagte die Protokolldatei zunächst nicht abschneidet. Wenn Sie dieses Verhalten wünschen, sollten Sie es hinzufügen
an den Anfang des Skripts.)
Die POSIX.1-2008-Spezifikation von
tee(1)
verlangt, dass die Ausgabe ungepuffert ist, dh nicht einmal zeilengepuffert. In diesem Fall ist es also möglich, dass STDOUT und STDERR in derselben Zeile von endenfoo.log
. jedoch könnte das auch auf dem Terminal passieren, so dass die Protokolldatei ein getreues Abbild der sein wird , was könnte auf dem Terminal zu sehen ist, wenn kein exakter Spiegel davon. Wenn Sie möchten, dass die STDOUT-Zeilen sauber von den STDERR-Zeilen getrennt werden, sollten Sie zwei Protokolldateien verwenden, möglicherweise mit Datumsstempelpräfixen in jeder Zeile, um später einen chronologischen Zusammenbau zu ermöglichen.quelle
exec > >(tee -a $LOG)
trap "kill -9 $! 2>/dev/null" EXIT
exec 2> >(tee -a $LOG >&2)
trap "kill -9 $! 2>/dev/null" EXIT
-i
zu gehentee
. Andernfalls stören Signalunterbrechungen (Traps) stdout im Skript. Wenn Sie beispielsweisetrap 'echo foo' EXIT
drücken und dann drückenctrl+c
, wird " foo " nicht angezeigt . Also würde ich die Antwort auf ändernexec > >(tee -ia foo.log)
.. log
oder verwenden. log foo.log
: sam.nipl.net/sh/log sam.nipl.net/sh/log-aSTDOUT
zuerst als Stapel und dann Nachrichten angezeigt werdenSTDERR
. Sie sind nicht wie gewöhnlich verschachtelt.Lösung für Busybox-, MacOS Bash- und Non-Bash-Shells
Die akzeptierte Antwort ist sicherlich die beste Wahl für Bash. Ich arbeite in einer Busybox-Umgebung ohne Zugriff auf Bash und verstehe die
exec > >(tee log.txt)
Syntax nicht. Es funktioniert auch nichtexec >$PIPE
richtig und versucht, eine normale Datei mit demselben Namen wie die Named Pipe zu erstellen, die fehlschlägt und hängt.Hoffentlich wäre dies für jemanden nützlich, der keine Bash hat.
Für jeden, der eine
rm $PIPE
Named Pipe verwendet, ist dies sicher , da dadurch die Verknüpfung der Pipe mit dem VFS aufgehoben wird. Die Prozesse, die sie verwenden, behalten jedoch bis zu ihrem Abschluss eine Referenzanzahl bei.Beachten Sie, dass die Verwendung von $ * nicht unbedingt sicher ist.
quelle
Fügen Sie in Ihrer Skriptdatei alle Befehle wie folgt in Klammern ein:
quelle
{}
)Einfache Möglichkeit, ein Bash-Skript-Protokoll in Syslog zu erstellen. Die Skriptausgabe ist sowohl über
/var/log/syslog
als auch über stderr verfügbar . syslog fügt nützliche Metadaten hinzu, einschließlich Zeitstempel.Fügen Sie diese Zeile oben hinzu:
Alternativ können Sie das Protokoll in eine separate Datei senden:
Dies erfordert
moreutils
(für dents
Befehl, der Zeitstempel hinzufügt).quelle
Mit der akzeptierten Antwort kehrte mein Skript immer wieder außergewöhnlich früh zurück (direkt nach 'exec >> (tee ...)') und ließ den Rest meines Skripts im Hintergrund laufen. Da ich diese Lösung nicht auf meine Weise zum Laufen bringen konnte, fand ich eine andere Lösung / Problemumgehung für das Problem:
Dadurch wird die Ausgabe des Skripts vom Prozess über die Pipe in den Sub-Hintergrundprozess von 'tee' geleitet, der alles auf der Disc und im ursprünglichen Standard des Skripts protokolliert.
Beachten Sie, dass 'exec &>' sowohl stdout als auch stderr umleitet. Wir können sie separat umleiten, wenn wir möchten, oder zu 'exec>' wechseln, wenn wir nur stdout möchten.
Auch wenn die Pipe zu Beginn des Skripts aus dem Dateisystem entfernt wird, funktioniert sie weiter, bis der Vorgang abgeschlossen ist. Wir können es einfach nicht mit dem Dateinamen nach der rm-Zeile referenzieren.
quelle
$logfile
Teil von nichttee < ${logfile}.pipe $logfile &
. Insbesondere habe ich versucht, dies zu ändern, um vollständig erweiterte Befehlsprotokollzeilen (vonset -x
) in die Datei zu erfassen , während nur Zeilen ohne führendes '+' in stdout angezeigt wurden, indem ich zu geändert habe,(tee | grep -v '^+.*$') < ${logfile}.pipe $logfile &
aber eine Fehlermeldung bezüglich erhalten habe$logfile
. Können Sie dietee
Zeile etwas genauer erklären ?Bash 4 verfügt über einen
coproc
Befehl, der eine Named Pipe für einen Befehl erstellt und die Kommunikation über diesen Befehl ermöglicht.quelle
Ich kann nicht sagen, dass ich mit einer der auf exec basierenden Lösungen zufrieden bin. Ich bevorzuge die direkte Verwendung von tee, daher rufe ich das Skript auf Anfrage selbst mit tee auf:
Dies ermöglicht Ihnen Folgendes:
Sie können dies anpassen, z. B. tee = false als Standard festlegen, TEE stattdessen die Protokolldatei halten lassen usw. Ich denke, diese Lösung ähnelt der von jbarlow, ist aber einfacher, vielleicht hat meine Lösung Einschränkungen, auf die ich noch nicht gestoßen bin.
quelle
Beides ist keine perfekte Lösung, aber hier sind einige Dinge, die Sie ausprobieren könnten:
oder
Die zweite würde eine Pipe-Datei herumliegen lassen, wenn etwas mit Ihrem Skript schief geht, was ein Problem sein kann oder nicht (dh Sie könnten
rm
es später in der übergeordneten Shell tun).quelle
tee
ich habe den Hintergrund vergessen - ich habe bearbeitet. Wie gesagt, beides ist keine perfekte Lösung, aber die Hintergrundprozesse werden beendet, wenn die übergeordnete Shell beendet wird, sodass Sie sich keine Sorgen machen müssen, dass sie Ressourcen für immer belasten.