Kannst du dich lächerlich machen UND stdout und stderr getrennt protokollieren?

Proble

So möchten Sie stdout und stderr (separat) eines Prozesses oder Unterprozesses protokollieren, ohne dass sich die Ausgabe von der unterscheidet, die Sie im Terminal sehen würden, wenn Sie nichts protokollieren würden.

Scheint ziemlich einfach, nein? Nun, leider scheint es nicht möglich zu sein, eine allgemeine Lösung für dieses Problem zu schreiben, die auf einem bestimmten Prozess funktioniert ...

Hintergrun

ie @ Pipe-Umleitung ist eine Methode, um stdout und stderr zu trennen, sodass Sie sie einzeln protokollieren können. Wenn Sie stdout / err in eine Pipe ändern, erkennt der Prozess möglicherweise, dass die Pipe keine tty ist (da sie keine Breite / Höhe, Baudrate usw. hat) und ändert möglicherweise ihr Verhalten entsprechend. Warum das Verhalten ändern? Nun, einige Entwickler nutzen Funktionen eines Terminals, die beim Schreiben in eine Datei keinen Sinn ergeben. Beispielsweise erfordern Ladebalken häufig, dass der Terminalcursor an den Zeilenanfang zurückbewegt wird und der vorherige Ladebalken mit einem Balken einer neuen Länge überschrieben wird. Auch Farbe und Schriftstärke können in einem Terminal angezeigt werden, in einer flachen ASCII-Datei jedoch nicht. Wenn Sie die Standardausgabe eines solchen Programms direkt in eine Datei schreiben würden, würde diese Ausgabe alle Terminal-ANSI-Escape-Codes enthalten und nicht die ordnungsgemäß formatierte Ausgabe. Der Entwickler implementiert daher eine Art "Isatty" -Prüfung, bevor er etwas in stdout / err schreibt, sodass Dateien einfacher ausgegeben werden können, wenn diese Prüfung false zurückgibt.

Die übliche Lösung besteht darin, solche Programme zu täuschen, dass die Pipes tatsächlich ttys sind, indem ein Pty verwendet wird - ein bidirektionales Pipe, das auch Breite, Höhe usw. hat Der Prozess wird zu einem echten Terminal (und Sie können es direkt in einer Datei protokollieren). Das einzige Problem ist, dass wir durch die Verwendung einer einzelnen Pty für stdout und stderr nicht mehr zwischen den beiden unterscheiden können.

So möchten Sie vielleicht eine andere Pty für jede Pipe ausprobieren - eine für die stdin, eine für die stdout und eine für die stderr. Obwohl dies in 50% der Fälle funktioniert, führen viele Prozesse leider zusätzliche Umleitungsprüfungen durch, um sicherzustellen, dass der Ausgabepfad von stdout und stderr (/ dev / tty000x) identisch ist. Wenn dies nicht der Fall ist, muss eine Umleitung erfolgen, sodass Sie dasselbe Verhalten erhalten, als hätten Sie stderr und stdout ohne eine Pty-Anweisung weitergeleitet.

Sie denken vielleicht, dass diese übertriebene Überprüfung auf Umleitung ungewöhnlich ist, aber leider ist sie ziemlich weit verbreitet, da viele Programme anderen Code für die Überprüfung verwenden, wie dieses Codebit in OSX:

http: //src.gnu-darwin.org/src/bin/stty/util.

Herausforderun

Ich denke, der beste Weg, eine Lösung zu finden, besteht in der Form einer Herausforderung. Wenn jemand das folgende Skript ausführen kann (im Idealfall über Python, aber an dieser Stelle nehme ich alles), so dass stdout und stderr getrennt protokolliert werden, UND Sie haben es geschafft, zu glauben, dass es über ein tty ausgeführt wurde. du löst das problem :)

#!/usr/bin/python

import os
import sys

if sys.stdout.isatty() and sys.stderr.isatty() and os.ttyname(sys.stdout.fileno()) == os.ttyname(sys.stderr.fileno()):
    sys.stdout.write("This is a")
    sys.stderr.write("real tty :)")
else:
    sys.stdout.write("You cant fool me!")

sys.stdout.flush()
sys.stderr.flush()

Hinweis, dass eine Lösung wirklich für @ funktionieren sollirgendei Prozess, nicht nur dieser Code spezifisch. Das Überschreiben des sys / os-Moduls und die Verwendung von LD_PRELOAD sind sehr interessante Möglichkeiten, um die Herausforderung zu meistern, aber sie lösen das Herz des Problems nicht:)

Antworten auf die Frage(10)

Ihre Antwort auf die Frage