Ich habe fast alle verfügbaren ähnlichen Fragen ohne Erfolg untersucht.
Lassen Sie mich das Problem im Detail beschreiben:
Ich führe einige unbeaufsichtigte Skripte aus und diese können Standardausgaben und Standardfehlerzeilen erzeugen. Ich möchte sie in der genauen Reihenfolge erfassen, die von einem Terminalemulator angezeigt wird, und ihnen dann ein Präfix wie "STDERR:" und "STDOUT:" hinzufügen.
Ich habe versucht, Pfeifen und sogar epollbasierte Ansätze zu verwenden, ohne Erfolg. Ich denke, die Lösung wird in kleinen Mengen verwendet, obwohl ich kein Meister darin bin. Ich habe auch einen Blick in den Quellcode von Gnomes VTE geworfen , aber das war nicht sehr produktiv.
Idealerweise würde ich Go anstelle von Bash verwenden, um dies zu erreichen, aber ich war nicht in der Lage, dies zu tun. Scheint, als ob Pipes aufgrund der Pufferung automatisch die Einhaltung einer korrekten Zeilenreihenfolge verbieten.
Hat jemand etwas Ähnliches tun können? Oder ist es einfach unmöglich? Ich denke, wenn ein Terminal-Emulator das kann, dann nicht - vielleicht indem Sie ein kleines C-Programm erstellen, das die PTYs anders behandelt?
Idealerweise würde ich asynchrone Eingaben verwenden, um diese 2 Streams (STDOUT und STDERR) zu lesen und sie dann nach Bedarf erneut auszudrucken, aber die Reihenfolge der Eingaben ist entscheidend!
ANMERKUNG: Ich kenne stderred, aber es funktioniert nicht mit Bash-Skripten und kann nicht einfach bearbeitet werden, um ein Präfix hinzuzufügen (da es im Grunde viele Syscalls umschließt).
Update: unten zwei Gists hinzugefügt
(Zufällige Verzögerungen von weniger als einer Sekunde können in dem von mir bereitgestellten Beispielskript hinzugefügt werden, um ein konsistentes Ergebnis zu beweisen.)
Update: Die Lösung dieser Frage würde auch diese andere Frage lösen , wie @Gilles hervorhob. Ich bin jedoch zu dem Schluss gekommen, dass es nicht möglich ist, das zu tun, was hier und da gefragt wurde. Bei der Verwendung 2>&1
beiden Ströme sind korrekt am pty / Rohr Ebene verschmolzen, sondern die Ströme getrennt und in der richtigen Reihenfolge sollte man den Ansatz in der Tat zu verwenden , verwendet stderred dass involes syscall Einhaken und wie zu sehen sind schmutzig in vielerlei Hinsicht.
Ich werde diese Frage gerne aktualisieren, wenn jemand das oben Gesagte widerlegen kann.
quelle
Antworten:
Sie könnten Koprozesse verwenden. Einfacher Wrapper, der beide Ausgaben eines bestimmten Befehls an zwei
sed
Instanzen (eine fürstderr
die andere fürstdout
) weiterleitet, die das Tagging ausführen.Beachten Sie mehrere Dinge:
Es ist eine magische Beschwörung für viele Menschen (einschließlich mich) - aus einem Grund (siehe die verknüpfte Antwort unten).
Es gibt keine Garantie, dass nicht gelegentlich ein paar Leitungen vertauscht werden - alles hängt von der Planung der Coprozesse ab. Eigentlich ist fast garantiert, dass es irgendwann wird. Das heißt, wenn Sie die Reihenfolge streng gleich halten, müssen Sie die Daten von beiden
stderr
undstdin
in demselben Prozess verarbeiten, sonst kann (und wird) der Kernel-Scheduler ein Durcheinander daraus machen.Wenn ich das Problem richtig verstehe, bedeutet dies, dass Sie die Shell anweisen müssen, beide Streams auf einen Prozess umzuleiten (dies kann mit AFAIK durchgeführt werden). Die Schwierigkeit beginnt, wenn dieser Prozess zu entscheiden beginnt, was zuerst zu tun ist - er müsste beide Datenquellen abfragen und irgendwann in den Zustand versetzt werden, in dem er einen Datenstrom verarbeiten würde und Daten in beiden Datenströmen ankommen, bevor er beendet wird. Und genau dort bricht es zusammen. Dies bedeutet auch, dass das Umbrechen der Ausgabesyscalls
stderred
wahrscheinlich der einzige Weg ist, um das gewünschte Ergebnis zu erzielen (und selbst dann kann es zu Problemen kommen, wenn auf einem Multiprozessorsystem ein Multithreading auftritt).In Bezug auf Coprocesses lesen Sie unbedingt die ausgezeichnete Antwort von Stéphane in Wie verwenden Sie den Befehl coproc in Bash? für einen tiefen Einblick.
quelle
./test1.sh: 3: ./test1.sh: Syntax error: "(" unexpected
durch Kopieren / Einfügen Ihres Skripts)bash
mit/bin/sh
(nicht sicher, warum ich es dort hatte).eval $@
ist ziemlich fehlerhaft. Verwenden"$@"
Sie diese Option, wenn Sie Ihre Argumente als exakte Befehlszeile ausführen möchten. Durch Hinzufügen einereval
Interpretationsebene wird eine Reihe von schwer vorhersehbaren (und möglicherweise böswilligen) Elementen hinzugefügt , wenn Sie Dateinamen oder andere Inhalte übergeben, die Sie nicht als steuern Argumente), Verhaltensweisen und nicht einmal mehr zitieren (Namen mit Leerzeichen in mehrere Wörter aufteilen, Globs erweitern, selbst wenn sie zuvor als wörtlich zitiert wurden, usw.).eval
, in einer Variablen zu schließen Filedeskriptoren benannt.exec {SEDo[1]}>&-
wird so funktionieren wie sie ist (ja, das Fehlen einer$
vor dem{
war absichtlich).Methode 1. Verwenden von Dateideskriptoren und awk
Wie wäre es mit so etwas, wenn Sie die Lösungen aus diesem SO Q & A mit dem Titel: Gibt es ein Unix-Dienstprogramm, mit dem Zeitstempel vor Textzeilen eingefügt werden können? und diese SO Q & A mit dem Titel: Pipe STDOUT und STDERR zu zwei verschiedenen Prozessen im Shell-Skript? .
Die Vorgehensweise
In Schritt 1 erstellen wir zwei Funktionen in Bash, die die Zeitstempelnachricht ausführen, wenn sie aufgerufen werden:
In Schritt 2 würden Sie die oben genannten Funktionen wie folgt verwenden, um die gewünschte Nachricht zu erhalten:
Beispiel
Hier habe ich ein Beispiel zusammengestellt, das
a
in STDOUT schreibt, 10 Sekunden ruht und dann die Ausgabe in STDERR schreibt. Wenn wir diese Befehlssequenz in unser obiges Konstrukt einfügen, erhalten wir die von Ihnen angegebene Nachricht.Methode # 2. Mit Annotate-Ausgabe
Es gibt ein Tool namens
annotate-output
, das Teil derdevscripts
Pakets ist und das tut, was Sie wollen. Es ist nur eine Einschränkung, dass es die Skripte für Sie ausführen muss.Beispiel
Wenn wir unsere obige Beispielbefehlssequenz in ein Skript mit folgendem Namen
mycmds.bash
einfügen:Wir können es dann so ausführen:
Das Ausgabeformat kann für den Timestamp-Teil gesteuert werden, jedoch nicht darüber hinaus. Aber es ist ähnlich wie das, wonach Sie suchen, sodass es genau Ihren Vorstellungen entspricht.
quelle
stderred
Ihnen kann die Grenzen von Linien nicht leicht bestimmen (dies zu versuchen wäre hackisch). Ich wollte sehen, ob mir jemand bei diesem Problem helfen kann, aber anscheinend möchte jeder die einzige Einschränkung ( Reihenfolge ) aufgeben, die der Frage zugrunde liegt