Ich verwende ein Python-Skript als Treiber für einen Hydrodynamik-Code. Wenn es Zeit ist, die Simulation auszuführen, verwende ich subprocess.Popen
, um den Code auszuführen, die Ausgabe von stdout und stderr in einem subprocess.PIPE
--- zu sammeln, dann kann ich die Ausgabeinformationen drucken (und in einer Protokolldatei speichern) und auf Fehler prüfen . Das Problem ist, ich habe keine Ahnung, wie der Code voranschreitet. Wenn ich es direkt über die Befehlszeile ausführe, gibt es mir eine Ausgabe darüber, zu welcher Iteration es ist, zu welcher Zeit, wie der nächste Zeitschritt ist usw.
Gibt es eine Möglichkeit, sowohl die Ausgabe zu speichern (zur Protokollierung und Fehlerprüfung) als auch eine Live-Streaming-Ausgabe zu erstellen?
Der relevante Abschnitt meines Codes:
ret_val = subprocess.Popen( run_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True )
output, errors = ret_val.communicate()
log_file.write(output)
print output
if( ret_val.returncode ):
print "RUN failed\n\n%s\n\n" % (errors)
success = False
if( errors ): log_file.write("\n\n%s\n\n" % errors)
Ursprünglich war kochend ich das run_command
durch , tee
so dass eine Kopie ging direkt in die Log-Datei, und der Strom noch Ausgang direkt mit dem Terminal - aber auf diese Weise ich keine Fehler (zu meinem know) speichern kann.
Bearbeiten:
Vorübergehende Lösung:
ret_val = subprocess.Popen( run_command, stdout=log_file, stderr=subprocess.PIPE, shell=True )
while not ret_val.poll():
log_file.flush()
Führen Sie dann in einem anderen Terminal tail -f log.txt
(st log_file = 'log.txt'
) aus.
quelle
Popen.poll
wie in einer vorherigen Frage zum Stapelüberlauf verwenden .git
), tun dies nur, wenn ihre Ausgabe ein "tty-Gerät" ist (getestet über libcisatty()
). In diesem Fall müssen Sie möglicherweise eine Pseudo-Tty öffnen.flush
, und es ist notwendig aus dem Stderr Rohr zu lesen , wenn der Subprozess viel stderr Ausgabe erzeugt. Es gibt nicht genug Platz in einem Kommentarfeld, um dies zu erklären ...Antworten:
Sie haben zwei Möglichkeiten, dies zu tun, indem Sie entweder einen Iterator aus den Funktionen
read
oder erstellen und Folgendesreadline
tun:oder
Oder Sie können eine
reader
und einewriter
Datei erstellen . Übergeben Sie daswriter
anPopen
und lesen Sie aus demreader
Auf diese Weise werden die Daten sowohl in
test.log
die Standardausgabe als auch in die Standardausgabe geschrieben.Der einzige Vorteil des Dateiansatzes besteht darin, dass Ihr Code nicht blockiert. So können Sie in der Zwischenzeit tun, was Sie wollen, und auf
reader
nicht blockierende Weise lesen, wann immer Sie wollen . Wenn Sie verwendenPIPE
, werdenread
undreadline
Funktionen blockiert, bis entweder ein Zeichen in die Pipe oder eine Zeile in die Pipe geschrieben wird.quelle
iter(process.stdout.readline, b'')
(dh der an iter übergebene Sentinel muss eine binäre Zeichenfolge sein, dab'' != ''
.for line in iter(process.stdout.readline, b''): sys.stdout.buffer.write(line)
process = subprocess.Popen(command, stderr=subprocess.STDOUT, stdout=subprocess.PIPE) for line in iter(process.stdout.readline, b'') sys.stdout.write(line.decode(sys.stdout.encoding))
Executive Summary (oder "tl; dr" -Version): Es ist einfach, wenn es höchstens eine gibt
subprocess.PIPE
, sonst ist es schwierig.Es kann an der Zeit sein, ein wenig darüber zu erklären, wie es
subprocess.Popen
funktioniert.(Vorsichtsmaßnahme: Dies ist für Python 2.x, obwohl 3.x ähnlich ist. Bei der Windows-Variante bin ich ziemlich verschwommen. Ich verstehe das POSIX-Zeug viel besser.)
Die
Popen
Funktion muss etwas gleichzeitig mit null bis drei E / A-Streams umgehen. Diese werden bezeichnetstdin
,stdout
undstderr
wie üblich.Sie können angeben:
None
Dies zeigt an, dass Sie den Stream nicht umleiten möchten. Diese werden stattdessen wie gewohnt geerbt. Beachten Sie, dass dies zumindest auf POSIX-Systemen nicht bedeutet, dass Pythons verwendet wirdsys.stdout
, sondern nur Pythons tatsächliche Standardausgabe. siehe Demo am Ende.int
Wert. Dies ist ein "roher" Dateideskriptor (zumindest unter POSIX). (Randnotiz:PIPE
undSTDOUT
sind eigentlichint
s intern, aber "unmögliche" Deskriptoren, -1 und -2.)fileno
Methode.Popen
findet den Deskriptor für diesen Stream unter Verwendung vonstream.fileno()
und fährt dann wie für einenint
Wert fort.subprocess.PIPE
Dies gibt an, dass Python eine Pipe erstellen soll.subprocess.STDOUT
(stderr
nur für ): Weisen Sie Python an, denselben Deskriptor wie für zu verwendenstdout
. Dies ist nur dann sinnvoll, wenn Sie einen (Nicht-None
) Wert für angegeben habenstdout
, und selbst dann wird er nur benötigt, wenn Sie ihn festlegenstdout=subprocess.PIPE
. (Andernfalls können Sie nur das gleiche Argument angeben, für das Sie angegeben habenstdout
, zPopen(..., stdout=stream, stderr=stream)
. B. )Die einfachsten Fälle (keine Rohre)
Wenn Sie nichts umleiten (alle drei als Standardwert
None
belassen oder explizit angebenNone
),Pipe
ist dies recht einfach. Es muss nur den Unterprozess abspalten und laufen lassen. Oder, wenn Sie zu einem nicht umleitenPIPE
-anint
oder ein Strom istfileno()
-es ist immer noch einfach, da das Betriebssystem die ganze Arbeit macht. Python muss nur den Unterprozess abspalten und stdin, stdout und / oder stderr mit den bereitgestellten Dateideskriptoren verbinden.Der immer noch einfache Fall: ein Rohr
Wenn Sie nur einen Stream umleiten, ist das
Pipe
immer noch ziemlich einfach. Lassen Sie uns jeweils einen Stream auswählen und ansehen.Angenommen , Sie möchten einige liefern
stdin
, aber wirstdout
undstderr
gehen un-umgeleitet, oder gehen Sie zu einem Dateideskriptor. Als übergeordneter Prozess muss Ihr Python-Programm lediglichwrite()
Daten über die Pipe senden. Sie können dies selbst tun, z.oder Sie können die Standarddaten an übergeben
proc.communicate()
, was dann dasstdin.write
oben gezeigte tut . Es kommt keine Ausgabe zurück, escommunicate()
gibt also nur einen weiteren echten Job: Es schließt auch die Leitung für Sie. (Wenn Sie nicht anrufenproc.communicate()
, müssen Sie anrufenproc.stdin.close()
, um die Pipe zu schließen, damit der Unterprozess weiß, dass keine Daten mehr eingehen.)Angenommen , Sie möchten capture
stdout
aber verlassenstdin
undstderr
allein. Auch hier ist es einfach: Rufen Sie einfach anproc.stdout.read()
(oder gleichwertig), bis keine Ausgabe mehr erfolgt. Daproc.stdout()
es sich um einen normalen Python-E / A-Stream handelt, können Sie alle normalen Konstrukte verwenden, z.oder Sie können wieder verwenden
proc.communicate()
, was einfach dasread()
für Sie erledigt .Wenn Sie nur erfassen möchten
stderr
, funktioniert dies genauso wie beistdout
.Es gibt noch einen Trick, bevor es schwierig wird. Angenommen, Sie möchten erfassen
stdout
und auch erfassen,stderr
jedoch auf derselben Pipe wie stdout:In diesem Fall
subprocess
"betrügt"! Nun, es muss dies tun, damit es nicht wirklich schummelt: Es startet den Unterprozess mit seinem stdout und seinem stderr, die in den (einzelnen) Pipe-Deskriptor gerichtet sind, der auf seinen übergeordneten (Python) Prozess zurückgreift. Auf der übergeordneten Seite gibt es wieder nur einen einzigen Pipe-Deskriptor zum Lesen der Ausgabe. Alle "stderr" -Ausgaben werden in angezeigtproc.stdout
, und wenn Sie aufrufenproc.communicate()
, ist das stderr-Ergebnis (zweiter Wert im Tupel)None
keine Zeichenfolge.Die schweren Fälle: zwei oder mehr Rohre
Die Probleme treten alle auf, wenn Sie mindestens zwei Rohre verwenden möchten. Tatsächlich hat der
subprocess
Code selbst dieses Bit:Aber leider haben wir hier mindestens zwei und vielleicht drei verschiedene Pfeifen hergestellt, sodass die
count(None)
Renditen entweder 1 oder 0 sind. Wir müssen die Dinge auf die harte Tour machen.Unter Windows werden auf diese Weise
threading.Thread
Ergebnisse fürself.stdout
und gesammeltself.stderr
, und der übergeordnete Thread liefertself.stdin
Eingabedaten (und schließt dann die Pipe).Unter POSIX wird dies verwendet,
poll
falls verfügbar, andernfallsselect
, um die Ausgabe zu akkumulieren und die Standardeingabe zu liefern. All dies läuft im (einzelnen) übergeordneten Prozess / Thread.Hier werden Threads oder Poll / Select benötigt, um Deadlocks zu vermeiden. Angenommen, wir haben alle drei Streams in drei separate Pipes umgeleitet. Angenommen, es gibt eine kleine Begrenzung, wie viele Daten in eine Pipe gestopft werden können, bevor der Schreibvorgang unterbrochen wird, und darauf zu warten, dass der Lesevorgang die Pipe vom anderen Ende "bereinigt". Lassen Sie uns diese kleine Grenze nur zur Veranschaulichung auf ein einzelnes Byte setzen. (So funktionieren die Dinge tatsächlich, außer dass das Limit viel größer als ein Byte ist.)
Wenn der übergeordnete Prozess (Python) versucht, mehrere Bytes zu schreiben, z. B.
'go\n'
toproc.stdin
, geht das erste Byte ein, und das zweite bewirkt, dass der Python-Prozess angehalten wird und darauf wartet, dass der Unterprozess das erste Byte liest und die Pipe leert.Angenommen, der Unterprozess beschließt, ein freundliches "Hallo! Keine Panik!" Gruß. Das
H
geht in seine Standard-Pipe, aber dase
bewirkt , dass es angehalten wird und darauf wartet, dass sein Elternteil das liest,H
und leert die Standard-Pipe.Jetzt stecken wir fest: Der Python-Prozess schläft und wartet darauf, "go" zu beenden, und der Unterprozess schläft ebenfalls und wartet darauf, "Hallo! Keine Panik!" Zu beenden.
Der
subprocess.Popen
Code vermeidet dieses Problem beim Threading-or-Select / Poll. Wenn Bytes über die Pipes gehen können, gehen sie. Wenn dies nicht möglich ist, muss nur ein Thread (nicht der gesamte Prozess) in den Ruhezustand versetzt werden. Bei Auswahl / Abfrage wartet der Python-Prozess gleichzeitig auf "kann schreiben" oder "Daten verfügbar" und schreibt in den Standard des Prozesses Nur wenn Platz vorhanden ist und nur dann stdout und / oder stderr gelesen wird, wenn Daten bereit sind. Derproc.communicate()
Code (tatsächlich dort,_communicate
wo die haarigen Fälle behandelt werden) wird zurückgegeben, sobald alle Standarddaten (falls vorhanden) gesendet und alle Standard- und / oder Standarddaten gesammelt wurden.Wenn Sie beide
stdout
undstderr
zwei verschiedene Pipes lesen möchten (unabhängig von einerstdin
Umleitung), müssen Sie auch einen Deadlock vermeiden. Das Deadlock-Szenario ist hier anders - es tritt auf, wenn der Unterprozess etwas lang schreibt,stderr
während Sie Daten abrufenstdout
, oder umgekehrt -, aber es ist immer noch da.Die Demo
Ich habe versprochen zu demonstrieren, dass Python
subprocess
es unumgeleitet an das zugrunde liegende stdout schreibt, nichtsys.stdout
. Also, hier ist ein Code:Beim Ausführen:
Beachten Sie, dass die erste Routine fehlschlägt, wenn Sie hinzufügen
stdout=sys.stdout
, da einStringIO
Objekt keine hatfileno
. Beim zweiten wird das weggelassen,hello
wenn Sie hinzufügen,stdout=sys.stdout
dasys.stdout
umgeleitet wurdeos.devnull
.(Wenn Sie Python - Datei-Descriptor-1 umleiten, die subprocess werden diese Umleitung folgen. Der
open(os.devnull, 'w')
Aufruf erzeugt einen Strom , derfileno()
größer als 2)quelle
sys.stdout
) an Pythons stdout geht, nicht an das ( ) stdout des Python- Programmssys.
. Was ich zugebe, ist eine ... merkwürdige Unterscheidung. Gibt es einen besseren Weg, dies auszudrücken?asyncio
basierter Code, der den "harten Teil" (der mehrere Pipes gleichzeitig behandelt) auf tragbare Weise implementiert . Sie können es mit dem Codeteed_call()
vergleichen, der mehrere Threads ( ) verwendet, um dasselbe zu tun .Wir können auch den Standard-Datei-Iterator zum Lesen von stdout verwenden, anstatt das iter-Konstrukt mit readline () zu verwenden.
quelle
Wenn Sie Bibliotheken von Drittanbietern verwenden können, können Sie möglicherweise Folgendes verwenden
sarge
(Offenlegung: Ich bin der Betreuer). Diese Bibliothek ermöglicht den nicht blockierenden Zugriff auf Ausgabestreams aus Unterprozessen - sie ist über dassubprocess
Modul geschichtet .quelle
Lösung 1: Protokollieren
stdout
UNDstderr
gleichzeitig in EchtzeitEine einfache Lösung, die sowohl stdout als auch stderr gleichzeitig zeilenweise in Echtzeit in einer Protokolldatei protokolliert.
Lösung 2: Eine Funktion
read_popen_pipes()
, mit der Sie beide Pipes (stdout / stderr) gleichzeitig in Echtzeit durchlaufen könnenquelle
Eine gute, aber "schwere" Lösung ist die Verwendung von Twisted - siehe unten.
Wenn Sie bereit sind, nur mit etwas in dieser Richtung zu leben, sollte dies funktionieren:
(Wenn Sie read () verwenden, wird versucht, die gesamte "Datei" zu lesen, was nicht nützlich ist. Was wir hier wirklich verwenden könnten, ist etwas, das alle Daten liest, die sich gerade in der Pipe befinden.)
Man könnte auch versuchen, dies mit Threading zu erreichen, z.
Jetzt könnten wir möglicherweise auch stderr hinzufügen, indem wir zwei Threads haben.
Beachten Sie jedoch, dass die Subprozessdokumente davon abraten, diese Dateien direkt zu verwenden, und empfehlen die Verwendung
communicate()
(hauptsächlich bei Deadlocks, von denen ich denke, dass sie oben kein Problem darstellen). Die Lösungen sind etwas klobig, sodass das Subprozessmodul anscheinend nicht ganz den Anforderungen entspricht den Job (siehe auch: http://www.python.org/dev/peps/pep-3145/ ) und wir müssen uns etwas anderes ansehen.Eine aufwändigere Lösung ist die Verwendung von Twisted wie hier gezeigt: https://twistedmatrix.com/documents/11.1.0/core/howto/process.html
Die Art und Weise, wie Sie dies mit Twisted tun, besteht darin, Ihren Prozess mit
reactor.spawnprocess()
einem zu erstellen und bereitzustellenProcessProtocol
, das dann die Ausgabe asynchron verarbeitet. Der Twisted-Beispiel-Python-Code finden Sie hier: https://twistedmatrix.com/documents/11.1.0/core/howto/listings/process/process.pyquelle
read()
Aufruf, bis der Unterprozess beendet wird und der übergeordnete ProzessEOF
auf der Pipe empfängt .Zusätzlich zu all diesen Antworten könnte ein einfacher Ansatz auch folgender sein:
Durchlaufen Sie den lesbaren Stream, solange er lesbar ist. Wenn ein leeres Ergebnis angezeigt wird, stoppen Sie ihn.
Der Schlüssel hier ist, dass
readline()
eine Zeile (mit\n
am Ende) zurückgegeben wird, solange eine Ausgabe vorhanden ist und leer ist, wenn sie wirklich am Ende ist.Hoffe das hilft jemandem.
quelle
Basierend auf all dem oben genannten schlage ich eine leicht modifizierte Version (python3) vor:
None
Code:
quelle
returncode
Teil war in meinem Fall entscheidend.Es sieht so aus, als würde die zeilengepufferte Ausgabe für Sie funktionieren. In diesem Fall könnte etwas wie das Folgende passen. (Vorsichtsmaßnahme: Es ist nicht getestet.) Dadurch wird der Standardprozess des Unterprozesses nur in Echtzeit angezeigt. Wenn Sie sowohl stderr als auch stdout in Echtzeit haben möchten, müssen Sie etwas Komplexeres tun
select
.quelle
Warum nicht
stdout
direkt einstellensys.stdout
? Und wenn Sie auch in ein Protokoll ausgeben müssen, können Sie einfach die Schreibmethode von f überschreiben.quelle
stdout
Dateideskriptor auf den Dateideskriptor des übergebenen Dateiobjekts. Die Schreibmethode würde niemals aufgerufen werden (zumindest macht das der Unterprozess für stderr, ich denke, es ist dasselbe für stdout).Alle oben genannten Lösungen, die ich ausprobiert habe, konnten entweder die Ausgabe von stderr und stdout (mehrere Pipes) nicht trennen oder wurden für immer blockiert, wenn der Pipe-Puffer des Betriebssystems voll war. Dies geschieht, wenn der Befehl, den Sie ausführen, zu schnell ausgegeben wird (es gibt eine Warnung für Python) poll () Handbuch des Unterprozesses). Der einzige zuverlässige Weg, den ich gefunden habe, war durch Auswahl, aber dies ist eine reine Posix-Lösung:
quelle
Ähnlich wie bei früheren Antworten, aber die folgende Lösung funktionierte für mich unter Windows mit Python3, um eine gängige Methode zum Drucken und Anmelden in Echtzeit bereitzustellen ( Echtzeitausgabe mit Python ):
quelle
Ich denke, dass die
subprocess.communicate
Methode etwas irreführend ist: Sie füllt tatsächlich die stdout und stderr , die Sie in der angebensubprocess.Popen
.Doch aus der Lektüre ,
subprocess.PIPE
dass Sie die zur Verfügung stellen kannsubprocess.Popen
‚s stdout und stderr Parameter werden schließlich OS Rohrpuffer füllen und Deadlock Ihre Anwendung (vor allem , wenn Sie mehrere Prozesse / Threads haben , die verwendet werden müssensubprocess
).Meine vorgeschlagene Lösung besteht darin, stdout und stderr mit Dateien zu versorgen - und den Inhalt der Dateien zu lesen, anstatt aus dem Deadlocking zu lesen
PIPE
. Diese Dateien könnentempfile.NamedTemporaryFile()
- auf die auch zum Lesen zugegriffen werden kann, während sie von geschrieben werdensubprocess.communicate
.Unten finden Sie ein Beispiel für die Verwendung:
Und dies ist der Quellcode, ist bereit , verwendet werden , mit so vielen Kommentare , wie ich liefern könnte , zu erklären , was es tut:
Wenn Sie mit Python 2, vergewissern Sie sich zuerst die neueste Version des Installations subprocess32 Paket von pypi.
quelle
Hier ist eine Klasse, die ich in einem meiner Projekte verwende. Es leitet die Ausgabe eines Unterprozesses in das Protokoll um. Zuerst habe ich versucht, die Schreibmethode einfach zu überschreiben, aber das funktioniert nicht, da der Unterprozess sie niemals aufruft (die Umleitung erfolgt auf der Ebene des Dateideskriptors). Ich verwende also meine eigene Pipe, ähnlich wie im Subprozessmodul. Dies hat den Vorteil, dass die gesamte Protokollierungs- / Drucklogik im Adapter gekapselt ist und Sie einfach Instanzen des Protokollierers an Folgendes übergeben können
Popen
:subprocess.Popen("/path/to/binary", stderr = LogAdapter("foo"))
Wenn Sie keine Protokollierung benötigen, sondern nur verwenden möchten, können
print()
Sie natürlich große Teile des Codes entfernen und die Klasse kürzer halten. Man könnte es auch durch ein erweitern__enter__
und__exit__
Verfahren und rufen Siefinished
in__exit__
so dass Sie sie leicht als Kontext verwenden könnte.quelle
Keine der Pythonic-Lösungen hat bei mir funktioniert. Es stellte sich heraus, dass
proc.stdout.read()
oder ähnliches für immer blockieren kann.Deshalb benutze ich
tee
so:Diese Lösung ist praktisch, wenn Sie sie bereits verwenden
shell=True
.${PIPESTATUS}
Erfasst den Erfolgsstatus der gesamten Befehlskette (nur in Bash verfügbar). Wenn ich das weglassen&& exit ${PIPESTATUS}
würde, würde dies immer Null zurückgeben, da estee
nie fehlschlägt.unbuffer
Dies kann erforderlich sein, um jede Zeile sofort in das Terminal zu drucken, anstatt viel zu lange zu warten, bis der "Pipe-Puffer" gefüllt ist. Unbuffer verschluckt jedoch den Exit-Status von Assert (SIG Abort) ...2>&1
protokolliert auch stderror in der Datei.quelle