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 bezeichnet stdin
, stdout
und stderr
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 wird sys.stdout
, sondern nur Pythons tatsächliche Standardausgabe. siehe Demo am Ende.
- Ein
int
Wert. Dies ist ein "roher" Dateideskriptor (zumindest unter POSIX). (Randnotiz: PIPE
und STDOUT
sind eigentlich int
s intern, aber "unmögliche" Deskriptoren, -1 und -2.)
- Ein Stream - wirklich jedes Objekt mit einer
fileno
Methode. Popen
findet den Deskriptor für diesen Stream unter Verwendung von stream.fileno()
und fährt dann wie für einen int
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 verwenden stdout
. Dies ist nur dann sinnvoll, wenn Sie einen (Nicht- None
) Wert für angegeben haben stdout
, und selbst dann wird er nur benötigt, wenn Sie ihn festlegen stdout=subprocess.PIPE
. (Andernfalls können Sie nur das gleiche Argument angeben, für das Sie angegeben haben stdout
, z Popen(..., stdout=stream, stderr=stream)
. B. )
Die einfachsten Fälle (keine Rohre)
Wenn Sie nichts umleiten (alle drei als Standardwert None
belassen oder explizit angeben None
), Pipe
ist dies recht einfach. Es muss nur den Unterprozess abspalten und laufen lassen. Oder, wenn Sie zu einem nicht umleiten PIPE
-an int
oder ein Strom ist fileno()
-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 wir stdout
und stderr
gehen un-umgeleitet, oder gehen Sie zu einem Dateideskriptor. Als übergeordneter Prozess muss Ihr Python-Programm lediglich write()
Daten über die Pipe senden. Sie können dies selbst tun, z.
proc = subprocess.Popen(cmd, stdin=subprocess.PIPE)
proc.stdin.write('here, have some data\n') # etc
oder Sie können die Standarddaten an übergeben proc.communicate()
, was dann das stdin.write
oben gezeigte tut . Es kommt keine Ausgabe zurück, es communicate()
gibt also nur einen weiteren echten Job: Es schließt auch die Leitung für Sie. (Wenn Sie nicht anrufen proc.communicate()
, müssen Sie anrufen proc.stdin.close()
, um die Pipe zu schließen, damit der Unterprozess weiß, dass keine Daten mehr eingehen.)
Angenommen , Sie möchten capture stdout
aber verlassen stdin
und stderr
allein. Auch hier ist es einfach: Rufen Sie einfach an proc.stdout.read()
(oder gleichwertig), bis keine Ausgabe mehr erfolgt. Da proc.stdout()
es sich um einen normalen Python-E / A-Stream handelt, können Sie alle normalen Konstrukte verwenden, z.
for line in proc.stdout:
oder Sie können wieder verwenden proc.communicate()
, was einfach das read()
für Sie erledigt .
Wenn Sie nur erfassen möchten stderr
, funktioniert dies genauso wie bei stdout
.
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:
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.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 angezeigt proc.stdout
, und wenn Sie aufrufen proc.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:
def communicate(self, input=None):
...
# Optimization: If we are only using one pipe, or no pipe at
# all, using select() or threads is unnecessary.
if [self.stdin, self.stdout, self.stderr].count(None) >= 2:
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ür self.stdout
und gesammelt self.stderr
, und der übergeordnete Thread liefert self.stdin
Eingabedaten (und schließt dann die Pipe).
Unter POSIX wird dies verwendet, poll
falls verfügbar, andernfalls select
, 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'
to proc.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 das e
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. Der proc.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
und stderr
zwei verschiedene Pipes lesen möchten (unabhängig von einer stdin
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 abrufen stdout
, 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, nicht sys.stdout
. Also, hier ist ein Code:
from cStringIO import StringIO
import os
import subprocess
import sys
def show1():
print 'start show1'
save = sys.stdout
sys.stdout = StringIO()
print 'sys.stdout being buffered'
proc = subprocess.Popen(['echo', 'hello'])
proc.wait()
in_stdout = sys.stdout.getvalue()
sys.stdout = save
print 'in buffer:', in_stdout
def show2():
print 'start show2'
save = sys.stdout
sys.stdout = open(os.devnull, 'w')
print 'after redirect sys.stdout'
proc = subprocess.Popen(['echo', 'hello'])
proc.wait()
sys.stdout = save
show1()
show2()
Beim Ausführen:
$ python out.py
start show1
hello
in buffer: sys.stdout being buffered
start show2
hello
Beachten Sie, dass die erste Routine fehlschlägt, wenn Sie hinzufügen stdout=sys.stdout
, da ein StringIO
Objekt keine hat fileno
. Beim zweiten wird das weggelassen, hello
wenn Sie hinzufügen, stdout=sys.stdout
da sys.stdout
umgeleitet wurde os.devnull
.
(Wenn Sie Python - Datei-Descriptor-1 umleiten, die subprocess werden diese Umleitung folgen. Der open(os.devnull, 'w')
Aufruf erzeugt einen Strom , der fileno()
größer als 2)
Popen.poll
wie in einer vorherigen Frage zum Stapelüberlauf verwenden .