Python-Unterprozess liest Stdout während der Ausführung

Salman Mehmood 8 Oktober 2023
  1. Python-Unterprozess liest stdout während der Ausführung
  2. Ansatz 1: Verwenden Sie check_call, um stdout eines subprocess zu lesen, während er in Python ausgeführt wird
  3. Ansatz 2: Abfragen des Prozesses zum Lesen von stdout eines subprocess während der Ausführung in Python
Python-Unterprozess liest Stdout während der Ausführung

Das Hauptziel dieses Artikels ist es zu zeigen, wie man die stdout eines subprocess liest, der in Python ausgeführt wird.

Python-Unterprozess liest stdout während der Ausführung

Wie bei vielen anderen eingebauten Modulen ist auch Subprocess ein eingebautes Modul, das bei einer “normalen” Python-Installation vorinstalliert ist.

Es wird hauptsächlich verwendet, wenn Sie Aufgaben, Prozesse und Programme in einem neuen Prozess ausführen, einen bestimmten Satz von Aufgaben ausführen und das Ergebnis zurückgeben möchten.

Einer der vielen Gründe für die breite Verwendung ist, dass es die Ausführung externer Programme und ausführbarer Dateien direkt aus dem Programm als separaten Prozess ermöglicht.

Beim Ausführen eines Programms mit der Bibliothek Subprocess kann es erforderlich sein, dass die Ausgabe dieses externen Programms in Echtzeit angezeigt wird. Dies kann aus vielen Gründen erforderlich sein, z. B. wenn ein Programm in Echtzeit läuft und nach kurzer Zeit von Berechnungen abhängt.

Betrachten Sie das folgende Programm:

import subprocess


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 Exception(command, exitCode, output)


if __name__ == "__main__":
    print(execute("cd C:\\ && C: && tree").decode("unicode_escape"))

Ausgang:

Folder PATH listing
Volume serial number is 0213-B7F2
C:.
+---DRIVERS
¦   +---bluetooth
¦       +---U1BLT07AVVASDSAP
¦           +---Custom
¦           ¦   +---EULA
¦           +---Win64
¦               +---LD
¦               +---svcpack
+---flutter
¦   +---.git
¦   ¦   +---hooks
¦   ¦   +---info
¦   ¦   +---logs
¦   ¦   ¦   +---refs
¦   ¦   ¦       +---heads
¦   ¦   ¦       +---remotes
¦   ¦   ¦           +---origin
¦   ¦   +---objects
¦   ¦   ¦   +---info
¦   ¦   ¦   +---pack
¦   ¦   +---refs
¦   ¦       +---heads
¦   ¦       +---remotes
¦   ¦       ¦   +---origin
¦   ¦       +---tags
¦   +---.github
¦   ¦   +---ISSUE_TEMPLATE
¦   ¦   +---workflows
¦   +---.idea
¦   ¦   +---runConfigurations
...

Wir können die Ausgabe im Programm sehen, aber nicht in Echtzeit. Die Ausgabe wird erst angezeigt, nachdem der gesamte Befehl (in unserem Fall Baum) ausgeführt wurde.

Die Ausgabe zeigt nichts, bis das Programm (oder der Befehl) in einem separaten Prozess mit dem Befehl Subprocess ausgeführt wird.

Da es in unserem Fall erforderlich ist, dass wir die Ausgabe in Echtzeit erhalten, müssen wir eine andere Lösung erstellen, die die Ausgabe des Programms zeigt, wie sie in die stdout geschrieben wird.

Die Lösung kann auf mehrere Arten angegangen werden, von denen einige unten erwähnt werden.

Ansatz 1: Verwenden Sie check_call, um stdout eines subprocess zu lesen, während er in Python ausgeführt wird

Betrachten Sie den folgenden Code:

import subprocess
import sys


def execute(command):
    subprocess.check_call(
        command, shell=True, stdout=sys.stdout, stderr=subprocess.STDOUT
    )


if __name__ == "__main__":
    print(execute("cd C:\\ && C: && tree").decode("unicode_escape"))

Ausgang:

Folder PATH listing
Volume serial number is 0213-B7F2
C:.
├───DRIVERS
│   └───bluetooth
│       └───U1BLT0720US14CMP
│           ├───Custom
│           │   └───EULA
│           └───Win64
│               ├───LD
│               └───svcpack
├───flutter
│   ├───.git
│   │   ├───hooks
│   │   ├───info
│   │   ├───logs
│   │   │   └───refs
│   │   │       ├───heads
│   │   │       └───remotes
│   │   │           └───origin
│   │   ├───objects
│   │   │   ├───info
│   │   │   └───pack
│   │   └───refs
│   │       ├───heads
│   │       ├───remotes
│   │       │   └───origin
│   │       └───tags
│   ├───.github
│   │   ├───ISSUE_TEMPLATE
│   │   └───workflows
│   ├───.idea
│   │   └───runConfigurations
│   ├───.pub-cache
│   │   ├───hosted
│   │   │   └───pub.dartlang.org
│   │   │       ├───.cache
...

Wenn die Hauptanforderung darin besteht, die Ausgabe des Programms in Echtzeit zu drucken, kann dies mit check_call erreicht werden. Diese einfache, saubere und elegante Lösung ist prägnant und “einfach perfekt” für sehr einfache Programme, z. B. wenn nur die Ausgabe gedruckt werden soll.

Der check_call unterstützt auch die Übergabe von Parametern. Wenn Ihr Programm also Argumente benötigt, um zu funktionieren, können diese einfach und problemlos an die Funktion übergeben werden. Diese Methode wartet auf den Abschluss des Programms.

Basierend auf dem Abschluss des Programms kehrt das Verfahren zurück; andernfalls wird eine CalledProcessError-Ausnahme ausgelöst. Der CalledProcessError enthält den Rückgabecode des Fehlers, auf den über das Attribut returncode zugegriffen werden kann.

Im oben erwähnten Code wird der Befehl an die Methode check_call übergeben, die den auszuführenden Befehl (bzw. das Programm) enthält.

Der Parameter shell wurde auf true gesetzt, um den Prozess mit einer Shell auszuführen, und die stdout des Prozesses ist auf die stdout unseres Programms gesetzt, sodass er direkt in unsere stdout schreibt, und wir können Sehen Sie sich die Änderungen in unserem stdout an.

Zuletzt wird stderr auf stdout des erzeugten Prozesses gesetzt.

Ansatz 2: Abfragen des Prozesses zum Lesen von stdout eines subprocess während der Ausführung in Python

Betrachten Sie den folgenden Code:

import subprocess
import sys


def execute(command):
    process = subprocess.Popen(
        command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
    )

    # Poll process for new output until finished
    while True:
        nextline = process.stdout.readline().decode("unicode_escape")
        if nextline == "" and process.poll() is not None:
            break
            sys.stdout.write(nextline)
            sys.stdout.flush()

        output = process.communicate()[0]
        exitCode = process.returncode

        if exitCode == 0:
            return output
        else:
            raise Exception(command, exitCode, output)


if __name__ == "__main__":
    print(execute("cd C:\\ && C: && tree").decode("unicode_escape"))

Ausgang:

...
¦   ¦   ¦       ¦   ¦       +---UuidUtil
¦   ¦   ¦       ¦   +---example
¦   ¦   ¦       ¦   +---lib
¦   ¦   ¦       ¦   +---test
¦   ¦   ¦       +---vector_math-2.1.2
¦   ¦   ¦       ¦   +---benchmark
¦   ¦   ¦       ¦   +---bin
¦   ¦   ¦       ¦   +---lib
¦   ¦   ¦       ¦   ¦   +---src
¦   ¦   ¦       ¦   ¦       +---vector_math
¦   ¦   ¦       ¦   ¦       ¦   +---third_party
¦   ¦   ¦       ¦   ¦       +---vector_math_64
¦   ¦   ¦       ¦   ¦       ¦   +---third_party
¦   ¦   ¦       ¦   ¦       +---vector_math_geometry
¦   ¦   ¦       ¦   ¦       ¦   +---filters
¦   ¦   ¦       ¦   ¦       ¦   +---generators
¦   ¦   ¦       ¦   ¦       +---vector_math_lists
¦   ¦   ¦       ¦   ¦       +---vector_math_operations
¦   ¦   ¦       ¦   +---test
¦   ¦   ¦       ¦   +---tool
¦   ¦   ¦       +---video_player-2.2.11
¦   ¦   ¦       ¦   +---android
¦   ¦   ¦       ¦   ¦   +---gradle
...

Um sicherzustellen, dass die Ausgabe des Programms, die mit dem Subprocess erzeugt wurde, gedruckt wird, sobald sie in die stdout geschrieben wird, müssen wir den Prozess für die Ausgabe abfragen und mit dem Lesen der letzten Zeile der stdout der stdout des Programms fortfahren.

In einer Endlos-Schleife lesen wir die Ausgabe des gespawnten Prozesses mit readline() weiter. Da die Ausgabe kodiert ist, müssen wir sie dekodieren (in unserem Fall utf-escape), um angemessen dargestellt zu werden.

Dies kann mit der Methode decode und Übergabe des entsprechenden Verschlüsselungsschemas erfolgen.

Sobald das Programm die Ausführung beendet hat, müssen wir auch die “Endlos”-Schleife verlassen. Wir können dies tun, indem wir zwei Dinge überprüfen:

  1. Die aktuelle stdout-Zeile ist leer.
  2. Der Prozess wurde beendet oder starb.

Das geht ganz einfach, mit einem einfachen String-Vergleich für die erste Bedingung und der Methode poll(), um zu prüfen, ob der Prozess beendet ist oder nicht. Ist das Programm beendet, gibt es den returncode zurück; andernfalls wird None zurückgegeben.

Ein einfacher Vergleich mit None kann uns auch Aufschluss darüber geben, ob das Programm mit der Ausführung fertig ist oder nicht.

Salman Mehmood avatar Salman Mehmood avatar

Hello! I am Salman Bin Mehmood(Baum), a software developer and I help organizations, address complex problems. My expertise lies within back-end, data science and machine learning. I am a lifelong learner, currently working on metaverse, and enrolled in a course building an AI application with python. I love solving problems and developing bug-free software for people. I write content related to python and hot Technologies.

LinkedIn

Verwandter Artikel - Python Subprocess