Python: Unterprozess.Anruf, stdout zu Datei stderr zu Datei -, Anzeige-stderr auf dem Bildschirm in Echtzeit
Ich habe ein Kommandozeilen-tool (eigentlich mehrere) , Schreibe ich einen wrapper für Python.
Das Werkzeug ist in der Regel wie folgt verwendet:
$ path_to_tool -option1 -option2 > file_out
Erhält der Benutzer die Ausgabe geschrieben file_out, und ist auch in der Lage, um zu sehen, diverse status-Meldungen von dem tool, wie es läuft.
Möchte ich replizieren dieses Verhalten, während auch die Protokollierung von stderr (Statusmeldungen) in eine Datei.
Was ich habe ist dieses:
from subprocess import call
call(['path_to_tool','-option1','option2'], stdout = file_out, stderr = log_file)
Dies funktioniert gut, AUßER, dass stderr nicht auf den Bildschirm geschrieben.
Ich kann hinzufügen von code zum drucken des Inhalts des log_file auf den Bildschirm natürlich, aber dann wird der Benutzer sehen, nachdem alles getan wird, statt, während es geschieht.
Zur Erinnerung, gewünschte Verhalten:
- call () oder subprocess()
- direkte stdout in eine Datei
- direkte stderr in eine Datei, während auch das schreiben stderr auf dem Bildschirm in Echtzeit, als ob die
Werkzeug berufen worden war, direkt von der Befehlszeile aus.
Habe ich das Gefühl ich bin entweder etwas fehlt wirklich einfach, oder ist das komplizierter als ich dachte...vielen Dank für jede Hilfe!!!
EDIT: dies muss nur auf Linux zu arbeiten.
InformationsquelleAutor der Frage Ben S. | 2013-08-20
Du musst angemeldet sein, um einen Kommentar abzugeben.
Du kann dies mit
subprocess
aber es ist nicht trivial. Wenn man sich die Häufig Verwendete Argumente in den docs, Sie werden sehen, dass Sie passieren kannPIPE
alsstderr
argument, das schafft eine neue Leitung, geht eine Seite von dem Rohr zu der Kind-Prozess, und lässt die andere Seite, die als diestderr
Attribut.*Also, werden Sie brauchen, Dienst, Leitung, schreiben auf den Bildschirm und in die Datei. Im Allgemeinen, der details ist für das ist sehr schwierig.** In Ihrem Fall gibt es nur ein Rohr, und Sie planen, über die Wartung synchron, so dass es nicht schlecht ist.
(Beachten Sie, dass es gibt einige Probleme mit
for line in proc.stderr:
—im Grunde, wenn das, was Sie gerade Lesen, stellt sich heraus, nicht zu sein, line-buffered-aus irgendeinem Grund, können Sie sitzen herum und warten für einen Zeilenumbruch, obwohl es eigentlich eine halbe Zeile Wert-Daten zu verarbeiten. Sie können Lesen chunks gleichzeitig mit, sagen wir,read(128)
oder sogarread(1)
um die Daten reibungslos, wenn nötig. Wenn Sie brauchen, um tatsächlich jedes byte, sobald es ankommt, und können es sich nicht leisten, die Kosten derread(1)
werden Sie brauchen, um das Rohr in den non-blocking Modus und Lesen asynchron.)Aber wenn du auf Unix, ist es möglicherweise einfacher zu verwenden, die
tee
- Befehl, um es für Sie tun.Für eine quick&dirty Lösung verwenden, können Sie die shell-Rohr durch. So etwas wie dieses:
Aber ich will nicht Debuggen von shell-Rohrleitungen; let ' s do it in Python, wie gezeigt in den docs:
Schließlich gibt es ein Dutzend oder mehr höher-level-Wrapper für Teilprozesse und/oder der shell auf PyPI—
sh
shell
shell_command
shellout
iterpipes
sarge
cmd_utils
commandwrapper
etc. Die Suche nach "shell", "Teilprozess", "Prozess", "Kommandozeile", etc. und finden, die Sie mögen, macht das problem trivial.Was, wenn Sie brauchen, um zu sammeln, sowohl stderr und stdout?
Den einfachen Weg, es zu tun, ist nur eine Umleitung auf die andere, wie Sven Marnach schlägt in einem Kommentar. Ändern Sie einfach die
Popen
Parameter wie folgt:Und dann überall verwendet werden
tool.stderr
verwendentool.stdout
statt—z.B., für das Letzte Beispiel:Aber dies hat einige Nachteile. Offensichtlich mischen der zwei Ströme zusammen bedeutet, dass Sie nicht log stdout zu file_out und stderr zu log_file, oder kopiere Standardausgabe auf Ihrem stdout und stderr zu Ihrem stderr. Aber es bedeutet auch, die Bestellung kann nicht deterministisch sein—wenn der Subprozess schreibt immer zwei Zeilen auf stderr vor dem schreiben etwas nach stdout, Sie könnten am Ende immer ein Bündel von stdout zwischen diesen beiden Linien, sobald Sie mischen die streams. Und es bedeutet, Sie teilen sich den stdout - buffering-Modus, so dass, wenn Sie sich auf die Tatsache, dass die linux/glibc-stderr garantiert, um sein line-buffered (es sei denn, der Teilprozess ausdrücklich ändert es), das kann nicht mehr wahr sein.
Wenn Sie behandeln müssen, die zwei Prozesse getrennt, es wird schwieriger. Früher habe ich gesagt, dass die Wartung der pipe on-the-fly ist einfach, solange man nur eine Leitung und kann es synchron. Wenn Sie zwei Rohre, das ist offensichtlich nicht mehr wahr. Stell dir vor, du wartest auf
tool.stdout.read()
und neue Daten, die vontool.stderr
. Wenn es zu viele Daten, es kann dazu führen, das Rohr zu überlaufen und den Unterprozess zu blockieren. Aber selbst wenn das nicht geschehen ist, können Sie offensichtlich nicht in der Lage zu Lesen und zu protokollieren stderr Daten, bis etwas kommt von stdout.Wenn Sie das pipe-through-
tee
Lösung, die vermeidet, dass das anfängliche problem... aber nur, indem Sie ein neues Projekt erstellen, das ist genauso schlimm. Sie haben zweitee
Instanzen, und während Sie anrufencommunicate
auf der einen, die anderen sitzen herum und warten für immer.So, so oder so, müssen Sie irgendeine Art von asynchroner Mechanismus. Sie können dies tun, ist mit threads, ein
select
Reaktor, so etwas wiegevent
usw.Hier ist eine quick-and-dirty-Beispiel:
Allerdings gibt es einige Sonderfälle, wo das nicht funktioniert. (Das problem ist die Reihenfolge, in der SIGCHLD und SIGPIPE/EPIPE/EOF ankommen. Ich denke keine, die Auswirkungen auf uns hier, da wir verschicken keine Eingabe... aber nicht das können Sie mir glauben, ohne zu denken es durch und/oder zu testen.) Die
Unterprozess.kommunizieren
Funktion von 3,3+ bekommt alle fummeligen details. Aber Sie finden es viel einfacher zu verwenden, eine der async-Teilprozess-wrapper-Implementierungen finden Sie auf PyPI und ActiveState, oder sogar den Teilprozess Sachen von einer vollwertigen async-framework wie Verdreht.* Die docs nicht wirklich erklären, was die Rohre sind, fast als ob Sie erwarten, dass Sie eine alte Unix-C hand... Aber einige der Beispiele, insbesondere in der Ersatz Älterer Funktionen mit der
subprocess
- Modul Abschnitt zeigen, wie Sie verwendet werden, und es ist ziemlich einfach.** Der schwierige Teil ist die Sequenzierung von zwei oder mehr Rohre richtig. Wenn Sie warten auf ein Rohr, die anderen dürfen überlauf und block, der verhindert, warten, auf das andere je zu Ende. Die einzige einfache Möglichkeit, dies zu umgehen, erstellen Sie einen thread, um jedes Rohr. (Auf den meisten *nix-Plattformen, die Sie verwenden können, eine
select
oderpoll
Reaktor statt, aber die cross-Plattform ist unglaublich schwer.) Die Quelle , um das Modul vor allemcommunicate
und seine Helfer, zeigt, wie es zu tun. (Ich verlinkte auf 3.3, weil in früheren Versionencommunicate
selbst bekommt einige wichtige Dinge falsch...) Das ist, warum, Wann immer möglich, die Sie verwenden möchtencommunicate
wenn Sie mehr als eine Pfeife. In Ihrem Fall, Sie können nichtcommunicate
aber zum Glück brauchen Sie nicht mehr als ein Rohr.InformationsquelleAutor der Antwort abarnert
Ich denke was du suchst ist etwas wie:
Werden, um die Ausgabe/Protokoll in eine Datei geschrieben, die ich ändern würde mein
cmdline
zu zählen üblichen Umleitungen, wie es gemacht werden würde, die auf einem simplen linux bash/shell. Zum Beispiel würde ich anfügentee
auf der Kommandozeile:cmdline += ' | tee -a logfile.txt'
Hoffe, das hilft.
InformationsquelleAutor der Antwort Brandt
Musste ich ein paar änderungen an @abarnert Antwort für Python 3. Dies scheint zu funktionieren:
InformationsquelleAutor der Antwort Timmmm