Um Programme von meinen Python-Skripten aus zu starten, verwende ich die folgende Methode:
def execute(command):
process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
output = process.communicate()[0]
exitCode = process.returncode
if (exitCode == 0):
return output
else:
raise ProcessException(command, exitCode, output)
Wenn ich also einen Prozess wie Process.execute("mvn clean install")
starte, wartet mein Programm, bis der Prozess abgeschlossen ist, und erst dann erhalte ich die vollständige Ausgabe meines Programms. Dies ist ärgerlich, wenn ich einen Prozess ausführe, dessen Abschluss eine Weile dauert.
Kann ich mein Programm die Prozessausgabe zeilenweise schreiben lassen, indem ich die Prozessausgabe abfrage, bevor sie in einer Schleife oder so endet?
** [BEARBEITEN] Entschuldigung, ich habe nicht sehr gut gesucht, bevor ich diese Frage gestellt habe. Threading ist eigentlich der Schlüssel. Hier finden Sie ein Beispiel, das zeigt, wie es geht: ** Python Subprocess.Popen aus einem Thread
quelle
Antworten:
Sie können iter verwenden , um Zeilen zu verarbeiten, sobald der Befehl sie ausgibt :
lines = iter(fd.readline, "")
. Hier ist ein vollständiges Beispiel, das einen typischen Anwendungsfall zeigt (danke an @jfs für die Hilfe):quelle
for line in popen.stdout: print(line.decode(), end='')
. Verwenden Sie zur Unterstützung von Python 2 und 3 das Byte-Literal:b''
Andernfallslines_iterator
endet es nie mit Python 3.bufsize=1
(es kann die Leistung auf Python verbessern 2), in der Nähe derpopen.stdout
Rohr explizit (ohne für die Garbage Collection wartet der sich darum zu kümmern) und Erhöhungsubprocess.CalledProcessError
(wiecheck_call()
,check_output()
tun). Dieprint
Anweisung unterscheidet sich in Python 2 und 3: Sie können den Softspace-Hackprint line,
(Hinweis: Komma) verwenden, um zu vermeiden, dass alle Zeilenumbrüche wie in Ihrem Code verdoppelt unduniversal_newlines=True
Python 3 weitergegeben werden, um Text anstelle von bytebezogener Antwort zu erhalten .execute(["python", "-u", "child_thread.py"])
. Weitere Informationen: stackoverflow.com/questions/14258500/…Ok, ich habe es geschafft, es ohne Threads zu lösen (Vorschläge, warum die Verwendung von Threads besser wäre, sind willkommen), indem ich einen Ausschnitt aus dieser Frage verwendet habe. Abfangen eines Standardprozesses während der Ausführung
quelle
print line,
umsys.stdout.write(nextline); sys.stdout.flush()
Andernfalls würde es alle zwei Zeilen auszudrucken Dann wieder diese IPython die Notebook - Schnittstelle verwendet, so vielleicht etwas anderes geschah -.. Egal, expliziten Aufrufflush()
funktioniert.stderr=subprocess.STDOUT
zustderr=subprocess.PIPE
und rufen Sie dannprocess.stderr.readline()
aus der Schleife, scheine ich afoul die sehr laufen Deadlock , dass etwa in der Dokumentation für das gewarntsubprocess
Modul.stdout=subprocess.PIPE,stderr=subprocess.STDOUT
dies stderr erfasst, und ich glaube (aber ich habe es nicht getestet), dass es auch stdin erfasst.So drucken Sie die Ausgabe des Unterprozesses zeilenweise, sobald der Standardpuffer in Python 3 geleert ist:
Hinweis: Sie brauchen nicht
p.poll()
- die Schleife endet, wenn eof erreicht ist. Und das brauchen Sie nichtiter(p.stdout.readline, '')
- der Read-Ahead-Fehler ist in Python 3 behoben.Siehe auch Python: Streaming-Eingabe von subprocess.communicate () lesen .
quelle
sys.stdout.flush()
im übergeordneten Element benötigen - stdout ist zeilengepuffert, wenn es nicht in eine Datei / Pipe umgeleitet wird und der Druckline
den Puffer daher automatisch leert. Sie brauchensys.stdout.flush()
das Kind auch nicht - übergeben Sie-u
stattdessen die Befehlszeilenoption.>
dann ausführenpython -u your-script.py > some-file
. Hinweis:-u
Option, die ich oben erwähnt habe (keine Notwendigkeit zu verwendensys.stdout.flush()
).p.wait()
- es wird beim Verlassen deswith
Blocks aufgerufen . Verwenden Siep.returncode
.@tokland
habe deinen Code ausprobiert und für 3.4 korrigiert und windows dir.cmd ist ein einfacher dir-Befehl, der als cmd-Datei gespeichert wird
quelle
iter()
undend='\r\n'
sind unnötig. Python verwendet standardmäßig den universellen Zeilenumbruchmodus, dh, jeder'\n'
wird'\r\n'
während des Druckvorgangs übersetzt.'latin'
Wenn es sich wahrscheinlich um eine falsche Codierung handelt, können Sie dieuniversal_newlines=True
Textausgabe in Python 3 abrufen (dekodiert mit der bevorzugten Codierung des Gebietsschemas). Hören Sie nicht auf.poll()
, es könnten ungelesene Daten gepuffert sein. Wenn das Python-Skript in einer Konsole ausgeführt wird, ist seine Ausgabe zeilengepuffert. Sie können die Zeilenpufferung mit der-u
Option erzwingen - das brauchen Sieflush=True
hier nicht.Es gibt tatsächlich eine sehr einfache Möglichkeit, dies zu tun, wenn Sie nur die Ausgabe drucken möchten :
Hier zeigen wir den Unterprozess einfach auf unser eigenes Standardout und verwenden die vorhandene API für Erfolg oder Ausnahme.
quelle
shell=True
Für den Fall, dass jemand von beiden lesen
stdout
undstderr
gleichzeitig Threads verwenden möchte, habe ich mir Folgendes ausgedacht:Ich wollte dies nur teilen, als ich auf diese Frage kam und versuchte, etwas Ähnliches zu tun, aber keine der Antworten löste mein Problem. Hoffentlich hilft es jemandem!
Beachten Sie, dass in meinem Anwendungsfall ein externer Prozess den von uns durchgeführten Prozess beendet
Popen()
.quelle
Wenn Sie versuchen, die Antworten auf diese Frage zu erhalten, um die Standardausgabe aus einem Python-Skript zu erhalten, beachten Sie, dass Python die Standardausgabe puffert. Daher kann es eine Weile dauern, bis die Standardausgabe angezeigt wird.
Dies kann behoben werden, indem nach jedem Standardschreiben im Zielskript Folgendes hinzugefügt wird:
quelle
import
das andere Skript sein. Schauen Sie inmultiprocessing
oderthreading
wenn Sie eine parallelisierte Ausführung benötigen.subprocess.run("/path/to/python/executable", "pythonProgramToRun.py")
In Python> = 3.5
subprocess.run
funktioniert das Arbeiten für mich:(Das Abrufen der Ausgabe während der Ausführung funktioniert auch ohne
shell=True
) https://docs.python.org/3/library/subprocess.html#subprocess.runquelle
subprocess.run()
Aufruf wird erst zurückgegeben, wenn der Unterprozess beendet ist.>>> import subprocess; subprocess.run('top')
scheint auch "während der Ausführung" zu drucken (und oben wird nie beendet). Vielleicht begreife ich keinen subtilen Unterschied?stdout=subprocess.PIPE
, können Sie sie erst nachtop
Abschluss lesen . Ihr Python-Programm wird während der Ausführung des Unterprozesses blockiert.run
Methode funktioniert immer noch, wenn Sie nur daran interessiert sind , die Ausgabe so zu sehen, wie sie generiert wurde. Wenn Sie etwas mit der Ausgabe in Python asynchron machen möchten, haben Sie Recht, dass es nicht funktioniert.Um die ursprüngliche Frage zu beantworten, leitet IMO den Unterprozess am besten
stdout
direkt zu Ihrem Programm umstdout
(optional kann diesstderr
auch wie im folgenden Beispiel durchgeführt werden).quelle
stdout
undstderr
macht dasselbe mit weniger Code. Obwohl ich denke, explizit ist besser als implizit.Dieser PoC liest ständig die Ausgabe eines Prozesses und kann bei Bedarf aufgerufen werden. Es bleibt nur das letzte Ergebnis erhalten, alle anderen Ausgaben werden verworfen, wodurch verhindert wird, dass der PIPE nicht mehr genügend Speicher hat:
print_date.py
Ausgabe: Sie können deutlich sehen, dass nur im Intervall von ~ 2,5 Sekunden nichts dazwischen ausgegeben wird.
quelle
Dies funktioniert zumindest in Python3.4
quelle
Keine der Antworten hier ging auf alle meine Bedürfnisse ein.
Ein kleiner Hintergrund: Ich verwende einen ThreadPoolExecutor, um einen Thread-Pool zu verwalten, wobei jeder einen Unterprozess startet und diese gleichzeitig ausführt. (In Python2.7 sollte dies aber auch in neuerem 3.x funktionieren). Ich möchte Threads nicht nur zum Sammeln von Ausgaben verwenden, da ich möchte, dass so viele wie möglich für andere Dinge verfügbar sind (ein Pool von 20 Prozessen würde 40 Threads nur zum Ausführen verwenden; 1 für den Prozess-Thread und 1 für stdout ... und mehr, wenn du stderr willst, denke ich)
Ich entferne hier viele Ausnahmen und solche, so dass dies auf Code basiert , der in der Produktion funktioniert. Hoffentlich habe ich es beim Kopieren und Einfügen nicht ruiniert. Auch Feedback sehr willkommen!
Ich bin mir sicher, dass hier Overhead hinzugefügt wird, aber das ist in meinem Fall kein Problem. Funktionell macht es was ich brauche. Das einzige, was ich nicht gelöst habe, ist, warum dies perfekt für Protokollnachrichten funktioniert, aber ich sehe, dass einige
print
Nachrichten später und auf einmal angezeigt werden.quelle
In Python 3.6 habe ich Folgendes verwendet:
quelle
subprocess.call()
hat einige Warzen, die durch neuere Funktionen behoben werden. in Python 3.6 würden Sie dies normalerweise verwendensubprocess.run()
; Der Einfachheit halbersubprocess.check_output()
ist auch die ältere Wrapper-Funktion weiterhin verfügbar - sie gibt die tatsächliche Ausgabe des Prozesses zurück (dieser Code würde nur den Exit-Code zurückgeben, aber selbst dann etwas Undefiniertes drucken).