Ich habe ein Skript, das von nicht technischen Benutzern interaktiv ausgeführt wird. Das Skript schreibt Statusaktualisierungen in STDOUT, damit der Benutzer sicher sein kann, dass das Skript ordnungsgemäß ausgeführt wird.
Ich möchte, dass sowohl STDOUT als auch STDERR zum Terminal umgeleitet werden (damit der Benutzer sehen kann, dass das Skript funktioniert und ob ein Problem aufgetreten ist). Ich möchte auch, dass beide Streams in eine Protokolldatei umgeleitet werden.
Ich habe eine Reihe von Lösungen im Internet gesehen. Einige funktionieren nicht und andere sind schrecklich kompliziert. Ich habe eine praktikable Lösung entwickelt (die ich als Antwort eingeben werde), aber sie ist klobig.
Die perfekte Lösung wäre eine einzelne Codezeile, die in den Anfang eines Skripts eingefügt werden könnte, das beide Streams sowohl an das Terminal als auch an eine Protokolldatei sendet.
BEARBEITEN: Das Umleiten von STDERR zu STDOUT und das Weiterleiten des Ergebnisses an das Tee funktioniert, hängt jedoch davon ab, dass die Benutzer daran denken, die Ausgabe umzuleiten und weiterzuleiten. Ich möchte, dass die Protokollierung narrensicher und automatisch ist (weshalb ich die Lösung in das Skript selbst einbetten möchte.)
Antworten:
Verwenden Sie "tee", um zu einer Datei und dem Bildschirm umzuleiten. Abhängig von der verwendeten Shell müssen Sie stderr zuerst mit stdout umleiten
oder
In csh gibt es einen integrierten Befehl namens "script", der alles, was auf dem Bildschirm angezeigt wird, in einer Datei erfasst. Sie starten es, indem Sie "script" eingeben, dann alles tun, was Sie erfassen möchten, und dann Strg-D drücken, um die Skriptdatei zu schließen. Ich kenne kein Äquivalent für sh / bash / ksh.
Da Sie angegeben haben, dass dies Ihre eigenen sh-Skripte sind, die Sie ändern können, können Sie die Umleitung auch intern durchführen, indem Sie das gesamte Skript mit geschweiften Klammern oder Klammern umgeben
quelle
2>&1 | tee -a filename
wurde stderr nicht aus meinem Skript in die Datei gespeichert, aber es funktionierte einwandfrei, als ich den Befehl kopierte und in das Terminal einfügte! Der Bracket-Trick funktioniert jedoch einwandfrei.Ein halbes Jahrzehnt später ...
Ich glaube, dies ist die "perfekte Lösung", die das OP sucht.
Hier ist ein Einzeiler, den Sie oben in Ihr Bash-Skript einfügen können:
Hier ist ein kleines Skript, das seine Verwendung demonstriert:
(Hinweis: Dies funktioniert nur mit Bash. Es funktioniert nicht mit / bin / sh.)
Von hier aus angepasst ; Soweit ich weiß, hat das Original STDERR nicht in der Protokolldatei abgefangen. Mit einer Notiz von hier behoben .
quelle
Das Muster
Dadurch werden sowohl stdout als auch stderr separat umgeleitet und separate Kopien von stdout und stderr an den Anrufer (möglicherweise Ihr Terminal) gesendet.
In zsh wird erst mit der nächsten Anweisung fortgefahren, wenn die
tee
s beendet sind.In bash stellen Sie möglicherweise fest, dass die letzten Zeilen der Ausgabe nach der nächsten Anweisung erscheinen .
In beiden Fällen gehen die richtigen Bits an die richtigen Stellen.
Erläuterung
Hier ist ein Skript (gespeichert in ./example):
Hier ist eine Sitzung:
So funktioniert das:
tee
Prozesse werden gestartet, ihre Standards werden Dateideskriptoren zugewiesen. Da sie in Prozessersetzungen eingeschlossen sind , werden die Pfade zu diesen Dateideskriptoren im aufrufenden Befehl ersetzt. Jetzt sieht es ungefähr so aus:the_cmd 1> /proc/self/fd/13 2> /proc/self/fd/14
the_cmd
wird ausgeführt und schreibt stdout in den ersten Dateideskriptor und stderr in den zweiten.Im Bash-Fall wird
the_cmd
nach Abschluss sofort die folgende Anweisung ausgeführt (wenn Ihr Terminal der Anrufer ist, wird Ihre Eingabeaufforderung angezeigt).Im zsh-Fall
the_cmd
wartet die Shelltee
nach Abschluss auf den Abschluss beider Prozesse, bevor sie fortfährt. Mehr dazu hier .Der erste
tee
Prozess, der austhe_cmd
der Standardausgabe liest , schreibt eine Kopie dieser Standardausgabe zurück an den Anrufer, da dies dertee
Fall ist. Die Ausgänge werden nicht umgeleitet, sodass sie unverändert zum Anrufer zurückkehrenDer zweite
tee
Prozess wirdstdout
an den Anrufer weitergeleitetstderr
(was gut ist, da stdin austhe_cmd
dem stderr liest ). Wenn es also in seine Standardausgabe schreibt, gehen diese Bits in die Standardausgabe des Aufrufers.Dadurch wird stderr sowohl in den Dateien als auch in der Ausgabe des Befehls von stdout getrennt.
Wenn das erste Tee Fehler schreibt, werden sie sowohl in der stderr-Datei als auch im stderr des Befehls angezeigt. Wenn das zweite Tee Fehler schreibt, werden sie nur im stderr des Terminals angezeigt.
quelle
tee
ist auf dem betreffenden System verfügbar.) Der Fehler, den ich erhalte, lautet "Der Prozess kann nicht auf die Datei zugreifen, da sie von einem anderen Prozess verwendet wird."Das Umleiten von stderr zu stdout hängt dies auf Ihren Befehl an:
2>&1
Für die Ausgabe an das Terminal und die Anmeldung in einer Datei sollten Sie verwendentee
Beide zusammen würden so aussehen:
BEARBEITEN: Zum Einbetten in Ihr Skript würden Sie dasselbe tun. Also dein Drehbuch
würde enden als
quelle
EDIT: Ich sehe, ich wurde entgleist und beantwortete eine andere Frage als die gestellte. Die Antwort auf die eigentliche Frage befindet sich am Ende von Paul Tomblins Antwort. (Wenn Sie diese Lösung verbessern möchten, um stdout und stderr aus irgendeinem Grund getrennt umzuleiten, können Sie die hier beschriebene Technik verwenden.)
Ich wollte eine Antwort, die die Unterscheidung zwischen stdout und stderr bewahrt. Leider sind alle bisher gegebenen Antworten, die diese Unterscheidung bewahren, rassenanfällig: Sie riskieren, dass Programme unvollständige Eingaben sehen, wie ich in den Kommentaren ausgeführt habe.
Ich denke, ich habe endlich eine Antwort gefunden, die die Unterscheidung bewahrt, nicht rassenanfällig ist und auch nicht besonders fummelig.
Erster Baustein: stdout und stderr tauschen:
Zweiter Baustein: Wenn wir nur stderr filtern wollten (z. B. tee), könnten wir dies erreichen, indem wir stdout & stderr austauschen, filtern und dann zurück tauschen:
Jetzt ist der Rest einfach: Wir können entweder am Anfang einen Standardfilter hinzufügen:
oder am Ende:
Um mich davon zu überzeugen, dass beide oben genannten Befehle funktionieren, habe ich Folgendes verwendet:
Ausgabe ist:
und meine Eingabeaufforderung kommt
teed stderr: to stderr
erwartungsgemäß sofort nach dem " " zurück.Fußnote zu zsh :
Die obige Lösung funktioniert in Bash (und vielleicht einigen anderen Shells, da bin ich mir nicht sicher), aber in Zsh funktioniert sie nicht. Es gibt zwei Gründe, warum es in zsh fehlschlägt:
2>&3-
wird von zsh nicht verstanden. das muss umgeschrieben werden als2>&3 3>&-
So muss zum Beispiel meine zweite Lösung für zsh as neu geschrieben werden
{my_command 3>&1 1>&- 1>&2 2>&- 2>&3 3>&- | stderr_filter;} 3>&1 1>&- 1>&2 2>&- 2>&3 3>&- | stdout_filter
(was auch in bash funktioniert, aber schrecklich ausführlich ist).Auf der anderen Seite können Sie das mysteriöse integrierte implizite Abschlagen von zsh nutzen, um eine viel kürzere Lösung für zsh zu erhalten, bei der überhaupt kein Abschlag ausgeführt wird:
(Ich hätte aus den Dokumenten nicht erraten, dass das
>&1
und das2>&2
sind, was das implizite Abschlagen von zsh auslöst. Ich habe das durch Ausprobieren herausgefunden.)quelle
my_function
) in der verschachtelten stdout / stderr-Filterung verwenden? Ich habe{ { my_function || touch failed;} 3>&1 1>&2 2>&3- | stderr_filter;} 3>&1 1>&2 2>&3- | stdout_filter
es getan, aber es fühlt sich komisch an, eine Datei als Indikator für einen Fehler zu erstellen ...Verwenden Sie den
script
Befehl in Ihrem Skript (Man 1-Skript).Erstellen Sie ein Wrapper-Shellscript (2 Zeilen), das script () einrichtet und dann exit aufruft.
Teil 1: wrap.sh
Teil 2: realscript.sh
Ergebnis:
quelle
Verwenden Sie das Tee-Programm und dup stderr, um zu stdout.
quelle
Ich habe ein Skript namens "RunScript.sh" erstellt. Der Inhalt dieses Skripts ist:
Ich nenne es so:
Dies funktioniert, erfordert jedoch, dass die Skripte der Anwendung über ein externes Skript ausgeführt werden. Es ist ein bisschen klobig.
quelle
Ein Jahr später ist hier ein altes Bash-Skript zum Protokollieren von Daten. Beispiel:
teelog make ...
Protokolle unter einem generierten Protokollnamen (und siehe auch den Trick zum Protokollieren verschachteltermake
s).quelle