Ich verwende Pythons subprocess.communicate()
, um stdout aus einem Prozess zu lesen, der ungefähr eine Minute lang ausgeführt wird.
Wie kann ich jede Zeile dieses Prozesses stdout
in Streaming- Form ausdrucken , damit ich die Ausgabe so sehen kann, wie sie generiert wurde, aber trotzdem den Prozess blockieren kann, bevor ich fortfahre?
subprocess.communicate()
scheint alle Ausgaben auf einmal zu geben.
python
subprocess
Heinrich Schmetterling
quelle
quelle
Antworten:
Bitte beachten Sie, dass ich die Methode von JF Sebastian (unten) für besser halte .
Hier ist ein einfaches Beispiel (ohne Überprüfung auf Fehler):
Wenn es
ls
zu schnell endet, endet die while-Schleife möglicherweise, bevor Sie alle Daten gelesen haben.Sie können den Rest in stdout folgendermaßen abfangen:
quelle
while proc.poll() is None: time.sleep(0)
zu erstellen oder etwas in diesem Sinne. Grundsätzlich müssen Sie entweder sicherstellen, dass die Ausgabe-Newline das letzte ist, was der Prozess ausführt (weil Sie dem Interpreter keine Zeit zum erneuten Schleifen geben können), oder Sie müssen etwas "Phantasievolles" tun.So erhalten Sie die Ausgabe des Unterprozesses zeilenweise, sobald der Unterprozess seinen Standardpuffer geleert hat:
iter()
wird verwendet, um Zeilen zu lesen, sobald sie geschrieben wurden, um den Read-Ahead-Fehler in Python 2 zu umgehen .Wenn stdout des Unterprozesses im nicht interaktiven Modus eine Blockpufferung anstelle einer Zeilenpufferung verwendet (was zu einer Verzögerung der Ausgabe führt, bis der Puffer des Kindes voll ist oder vom Kind explizit geleert wird), können Sie versuchen, eine ungepufferte Ausgabe mit zu erzwingen
pexpect
,pty
Module oderunbuffer
,stdbuf
,script
Dienstprogramme , siehe F: Warum nicht einfach ein Rohr verwenden (popen ())?Hier ist Python 3-Code:
Hinweis: Im Gegensatz zu Python 2, das die Bytestrings des Unterprozesses unverändert ausgibt. Python 3 verwendet den Textmodus (die Ausgabe von cmd wird mithilfe der
locale.getpreferredencoding(False)
Codierung dekodiert ).quelle
b''
ist einbytes
Literal in Python 2.7 und Python 3.bufsize=1
Kann einen Unterschied machen, wenn Sie auch in den Unterprozess schreiben (verwendenp.stdin
), z. B. kann es helfen, einen Deadlock zu vermeiden, während Sie einen interaktiven (pexpect
ähnlichen) Austausch durchführen - vorausgesetzt, es gibt keine Pufferprobleme im untergeordneten Prozess selbst. Wenn Sie dann nur lesen, wie gesagt, liegt der Unterschied nur in der Leistung: Wenn dies nicht der Fall ist, können Sie dann ein minimales vollständiges Codebeispiel bereitstellen, das dies zeigt?stderr=subprocess.STDOUT
zuPopen()
). Siehe auch dort verknüpfte Threading- oder Asyncio-Lösungen .stdout=PIPE
die Ausgabe nicht erfasst wird (Sie sehen sie immer noch auf dem Bildschirm), druckt Ihr Programm möglicherweise stattdessen auf stderr oder direkt auf dem Terminal. Um stdout & stderr zusammenzuführen, übergeben Siestderr=subprocess.STDOUT
(siehe meinen vorherigen Kommentar). Um die direkt auf Ihrem tty gedruckte Ausgabe zu erfassen, können Sie pexpect, pty-Lösungen verwenden. . Hier ist ein komplexeres Codebeispiel .Ich glaube, der einfachste Weg, die Ausgabe eines Prozesses auf Streaming-Weise zu sammeln, ist folgender:
Die Funktion
readline()
oderread()
sollte nur eine leere Zeichenfolge in EOF zurückgeben, nachdem der Prozess beendet wurde. Andernfalls wird sie blockiert, wenn nichts zu lesen ist (readline()
einschließlich der neuen Zeile. Bei leeren Zeilen wird "\ n" zurückgegeben). Dies vermeidet die Notwendigkeit eines umständlichen letztencommunicate()
Aufrufs nach der Schleife.Bei Dateien mit sehr langen Zeilen ist es
read()
möglicherweise vorzuziehen, die maximale Speichernutzung zu reduzieren. Die an sie übergebene Anzahl ist willkürlich. Wenn Sie sie jedoch ausschließen, wird die gesamte Pipe-Ausgabe auf einmal gelesen, was wahrscheinlich nicht wünschenswert ist.quelle
data = proc.stdout.read()
blockiert, bis alle Daten gelesen sind. Sie könnten es damit verwechseln,os.read(fd, maxsize)
dass es früher zurückkehren kann (sobald Daten verfügbar sind).read()
funktioniert dies einwandfrei undreadline()
funktioniert ebenfalls einwandfrei, solange die maximale Zeilenlänge angemessen ist. Meine Antwort wurde entsprechend aktualisiert.Wenn Sie einen nicht blockierenden Ansatz wünschen, verwenden Sie ihn nicht
process.communicate()
. Wenn Sie dassubprocess.Popen()
Argumentstdout
auf setzenPIPE
, können Sie aus lesenprocess.stdout
und prüfen, ob der Prozess noch mit ausgeführt wirdprocess.poll()
.quelle
Wenn Sie einfach versuchen, die Ausgabe in Echtzeit weiterzuleiten, ist es schwierig, einfacher zu werden:
Siehe die Dokumente für subprocess.check_call () .
Wenn Sie die Ausgabe verarbeiten müssen, führen Sie eine Schleife durch. Aber wenn Sie dies nicht tun, halten Sie es einfach.
Bearbeiten: JF Sebastian weist darauf hin, dass die Standardeinstellungen für die Parameter stdout und stderr an sys.stdout und sys.stderr übergeben werden und dass dies fehlschlägt, wenn sys.stdout und sys.stderr ersetzt wurden (z. B. zum Erfassen der Ausgabe in) Tests).
quelle
sys.stdout
odersys.stderr
mit dateiähnliche Objekte ersetzt , die keinen wirklichen fileno haben (). Wennsys.stdout
,sys.stderr
nicht ersetzt werden , dann ist es noch einfacher:subprocess.check_call(args)
.call()
es,check_call()
wenn ich das nicht willCalledProcessError
.python -mthis
: "Fehler sollten niemals stillschweigend weitergegeben werden. Es sei denn, dies wird ausdrücklich zum Schweigen gebracht." Deshalb ist der Beispielcode bevorzugen solltecheck_call()
übercall()
.call()
abschließe, geben unter fehlerfreien Bedingungen Fehlercodes ungleich Null zurück, weil sie schrecklich sind. In unserem Fall ist ein Fehlercode ungleich Null also kein Fehler.grep
dieses, die möglicherweise den Exit-Status ungleich Null zurückgeben, auch wenn kein Fehler vorliegt - dies sind Ausnahmen. Standardmäßig zeigt der Exit-Status Null Erfolg an.quelle
shlex.split(myCommand)
anstelle von zu verwendenmyCommand.split()
. Leerzeichen werden auch in zitierten Argumenten berücksichtigt.