Ich versuche, ein Wrapper-Skript für ein Befehlszeilenprogramm (svnadmin verify) zu schreiben, das eine schöne Fortschrittsanzeige für den Vorgang anzeigt. Dies erfordert, dass ich jede Ausgabezeile des umschlossenen Programms sehen kann, sobald es ausgegeben wird.
Ich dachte mir, ich würde das Programm einfach ausführen subprocess.Popen
, verwenden stdout=PIPE
, dann jede Zeile beim Eingang lesen und entsprechend handeln. Als ich jedoch den folgenden Code ausführte, schien die Ausgabe irgendwo gepuffert zu sein, was dazu führte, dass sie in zwei Blöcken angezeigt wurde, den Zeilen 1 bis 332 und dann 333 bis 439 (die letzte Ausgabezeile).
from subprocess import Popen, PIPE, STDOUT
p = Popen('svnadmin verify /var/svn/repos/config', stdout = PIPE,
stderr = STDOUT, shell = True)
for line in p.stdout:
print line.replace('\n', '')
Nachdem ich mir die Dokumentation zum Unterprozess ein wenig angesehen hatte, entdeckte ich den bufsize
Parameter to Popen
und versuchte, die Puffergröße auf 1 (Puffer für jede Zeile) und 0 (kein Puffer) zu setzen, aber keiner der Werte schien die Art und Weise zu ändern, in der die Zeilen geliefert wurden.
Zu diesem Zeitpunkt fing ich an, nach Strohhalmen zu greifen, also schrieb ich die folgende Ausgabeschleife:
while True:
try:
print p.stdout.next().replace('\n', '')
except StopIteration:
break
bekam aber das gleiche Ergebnis.
Ist es möglich, eine Echtzeit-Programmausgabe eines Programms zu erhalten, das unter Verwendung eines Unterprozesses ausgeführt wird? Gibt es in Python eine andere Option, die vorwärtskompatibel ist (nicht exec*
)?
quelle
sydout=PIPE
damit der Unterprozess direkt auf Ihre Konsole schreibt und den übergeordneten Prozess umgeht?Antworten:
Ich habe es versucht, und aus irgendeinem Grund während des Codes
puffert aggressiv die Variante
nicht. Anscheinend ist dies ein bekannter Fehler: http://bugs.python.org/issue3907 (Das Problem ist ab dem 29. August 2018 "geschlossen")
quelle
while p.poll() is None
anstellewhile True
derif not line
print(line.decode('utf-8').rstrip())
.PYTHONUNBUFFERED=1
. Dies ist besonders nützlich für Ausgaben, die unendlich sindquelle
p.stdout.close()
unklar ist.Sie können die Unterprozessausgabe direkt an die Streams weiterleiten. Vereinfachtes Beispiel:
quelle
.communicate()
? Oder gehen die Inhalte für die übergeordneten stderr / stdout-Streams verloren?communicate()
Methode für die RückgabeCompletedProcess
. Auchcapture_output
schließt sich mitstdout
und gegenseitig ausstderr
.Sie können dies versuchen:
Wenn Sie readline anstelle von read verwenden, wird die Eingabenachricht in einigen Fällen nicht gedruckt. Probieren Sie es mit einem Befehl aus, für den eine Inline-Eingabe erforderlich ist, und überzeugen Sie sich selbst.
quelle
Der Streaming-Unterprozess stdin und stdout mit asyncio im Python- Blogbeitrag von Kevin McCarthy zeigt, wie es mit asyncio geht:
quelle
import nest_asyncio; nest_asyncio.apply()
und den Shell-Befehl verwenden, dhprocess = await create_subprocess_shell(*command, stdout=PIPE, stderr=PIPE, shell=True)
anstelle vonprocess = await create_subprocess_exec(...)
. Prost!Problem mit der Echtzeitausgabe behoben: Beim Erfassen der Echtzeitausgabe des c-Programms ist ein ähnliches Problem in Python aufgetreten. Ich fügte hinzu " fflush (stdout) ;" in meinem C-Code. Es hat bei mir funktioniert. Hier ist der Ausschnitt des Codes
<< C Programm >>
<< Python-Programm >>
<< AUSGABE >> Drucken: Anzahl 1 Druck: Anzahl 2 Druck: Anzahl 3
Ich hoffe es hilft.
~ Sairam
quelle
flush(stdout)
) in C ++ verwendet. Vielen Dank!Ich bin vor einiger Zeit auf dasselbe Problem gestoßen. Meine Lösung bestand darin, die Iteration für die
read
Methode aufzugeben , die sofort zurückgegeben wird, selbst wenn Ihr Unterprozess nicht vollständig ausgeführt wurde usw.quelle
Je nach Anwendungsfall möchten Sie möglicherweise auch die Pufferung im Unterprozess selbst deaktivieren.
Wenn der Unterprozess ein Python-Prozess ist, können Sie dies vor dem Aufruf tun:
Oder geben Sie dies alternativ in der
env
Argument anPopen
.Andernfalls können Sie unter Linux / Unix die verwenden
stdbuf
Tool verwenden. ZB wie:Siehe auch hier über
stdbuf
oder andere Optionen.(Siehe auch hier für die gleiche Antwort.)
quelle
Ich habe diese Lösung verwendet, um Echtzeitausgaben für einen Unterprozess zu erhalten. Diese Schleife wird beendet, sobald der Prozess abgeschlossen ist, sodass keine break-Anweisung oder mögliche Endlosschleife erforderlich ist.
quelle
if out=='': break
nachout = sub_process...
Ich habe diese "Plug-and-Play" -Funktion hier gefunden . Lief wie am Schnürchen!
quelle
stderr=subprocess.STDOUT
hilft tatsächlich sehr bei der Erfassung von Streaming-Daten. Ich stimme zu.Sie können einen Iterator über jedes Byte in der Ausgabe des Unterprozesses verwenden. Dies ermöglicht eine Inline-Aktualisierung (Zeilen, die mit '\ r' enden, überschreiben die vorherige Ausgabezeile) aus dem Unterprozess:
quelle
In Python 3.x kann der Prozess hängen bleiben, da die Ausgabe ein Byte-Array anstelle einer Zeichenfolge ist. Stellen Sie sicher, dass Sie es in eine Zeichenfolge dekodieren.
Ab Python 3.6 können Sie dies mit dem Parameter
encoding
in Popen Constructor tun . Das vollständige Beispiel:Beachten Sie, dass dieser Code zu Ausgabefehlern umleitet und diese behandelt .
stderr
stdout
quelle
Die Verwendung von pexpect [ http://www.noah.org/wiki/Pexpect ] mit nicht blockierenden Readlines löst dieses Problem. Dies ist darauf zurückzuführen, dass Pipes gepuffert werden und die Ausgabe Ihrer App daher von der Pipe gepuffert wird. Daher können Sie diese Ausgabe erst erreichen, wenn der Puffer voll ist oder der Prozess abbricht.
quelle
Komplette Lösung:
quelle
universal_newlines=True
denPopen()
Anruf verwenden, müssen Sie wahrscheinlich auch nicht selbst damit umgehen - das ist der springende Punkt der Option.Dies ist das Grundgerüst, das ich immer dafür benutze. Es erleichtert die Implementierung von Zeitüberschreitungen und ist in der Lage, unvermeidliche Hängeprozesse zu bewältigen.
quelle
(Diese Lösung wurde mit Python 2.7.15 getestet.)
Sie müssen nur sys.stdout.flush () nach jeder Zeile lesen / schreiben:
quelle
Nur wenige Antworten, die Python 3.x oder Pthon 2.x vorschlagen. Der folgende Code funktioniert für beide.
quelle