Tutorial

Verwenden eines Unterprozesses zum Ausführen externer Programme in Python 3

Published on September 11, 2020
Default avatar

By DavidMuller

Author of Intuitive Python

Deutsch
Verwenden eines Unterprozesses zum Ausführen externer Programme in Python 3

Der Autor hat den COVID-19 Relief Fund dazu ausgewählt, eine Spende im Rahmen des Programms Write for DOnations zu erhalten.

Einführung

Python 3 beinhaltet das subprocess-Modul zum Ausführen externer Programme und Lesen ihrer Ausgaben in Ihrem Python-Code.

Möglicherweise finden Sie subprocess nützlich, wenn Sie in Ihrem Python-Code auf Ihrem Computer ein anderes Programm verwenden möchten. Beispielsweise möchten Sie vielleicht git aus Ihrem Python-Code aufrufen, um Dateien in Ihrem Projekt abzurufen, die in der git-Versionskontrolle verfolgt werden. Da sich jedes Programm, das Sie auf Ihrem Computer aufrufen können, über subprocess steuern lässt, gelten die hier angegebenen Beispiele für externe Programme, die Sie ggf. über Ihren Python-Code aufrufen möchten.

subprocess enthält verschiedene Klassen und Funktionen; wir werden in diesem Tutorial jedoch eine der nützlichsten Funktionen von subprocess abdecken: subprocess.run. Wir werden uns die verschiedenen Einsatzmöglichkeiten und wichtigsten Schlüsselwortargumente ansehen.

Voraussetzungen

Um das Beste aus diesem Tutorial herauszuholen, empfiehlt es sich, eine gewisse Vertrautheit mit Programmierung in Python 3 aufzuweisen. Sie können sich für die erforderlichen Hintergrundinformationen folgende Tutorials ansehen:

Ausführen eines externen Programms

Sie können mit der Funktion subprocess.run ein externes Programm über Ihren Python-Code ausführen. Zuerst müssen Sie jedoch die Module subprocess und sys in Ihr Programm importieren:

import subprocess
import sys

result = subprocess.run([sys.executable, "-c", "print('ocean')"])

Wenn Sie dies ausführen, erhalten Sie eine Ausgabe wie die folgende:

Output
ocean

Sehen wir uns dieses Beispiel an:

  • sys.executable ist der absolute Pfad zur ausführbaren Python-Datei, mit der Ihr Programm ursprünglich aufgerufen wurde. Beispielsweise kann sys.executable ein Pfad wie /usr/local/bin/python sein.
  • An subprocess.run wird eine Liste mit Zeichenfolgen übergeben, die aus den Komponenten des Befehls besteht, den wir ausführen möchten. Da die erste Zeichenfolge, die wir übergeben, sys.executable ist, weisen wir subprocess.run an, ein neues Python-Programm auszuführen.
  • Die Komponente -c ist eine python-Befehlszeilenoption, mit der Sie eine Zeichenfolge mit einem gesamten Python-Programm zur Ausführung übergeben können. In unserem Fall übergeben wir ein Programm, das die Zeichenkette ocean ausgibt.

Sie können sich jeden Eintrag in der Liste, den wir an subprocess.run übergeben, als durch ein Leerzeichen getrennt vorstellen. Beispielsweise wird [sys.executable, "-c", "print('ocean')"] in etwa zu /usr/local/bin/python -c "print('ocean')". Beachten Sie, dass subprocess automatisch die Komponenten des Befehls angibt, bevor versucht wird, sie im zugrunde liegenden Betriebssystem auszuführen. So können Sie beispielsweise einen Dateinamen übergeben, der Leerzeichen enthält.

Warnung: Übergeben Sie nie unvertrauenswürdige Eingaben an subprocess.run. Da subprocess.run die Fähigkeit hat, beliebige Befehle auf Ihrem Computer auszuführen, können bösartige Akteure damit Ihren Computer auf unerwartete Weise manipulieren.

Erfassen von Ausgaben aus einem externen Programm

Nachdem wir mit subprocess.run ein externes Programm aufrufen können, sehen wir uns nun an, wie wir Ausgaben von diesem Programm erfassen können. Beispielsweise kann dieser Prozess nützlich sein, wenn wir git ls-files verwenden möchten, um alle aktuell in der Versionskontrolle gespeicherten Dateien auszugeben.

Anmerkung: Die in diesem Abschnitt angegebenen Beispiele benötigen Python 3.7 oder höher. Insbesondere wurden die Schlüsselwortargumente capture_output und text in Python 3.7 hinzugefügt, als es im Juni 2018 veröffentlicht wurde.

Ergänzen wir unser vorheriges Beispiel:

import subprocess
import sys

result = subprocess.run(
    [sys.executable, "-c", "print('ocean')"], capture_output=True, text=True
)
print("stdout:", result.stdout)
print("stderr:", result.stderr)

Wenn wir diesen Code ausführen, erhalten wir eine Ausgabe wie die folgende:

Output
stdout: ocean stderr:

Dieses Beispiel ist weitgehend das gleiche wie das im ersten Abschnitt eingeführte: Wir führen noch immer einen Unterprozess zum Ausdrucken von ocean aus. Wichtig:Wir übergeben jedoch die Schlüsselwortargumente capture_output=True und text=True an subprocess.run.

subprocess.run gibt ein subprocess.CompletedProcess-Objekt zurück, das an result gebunden ist. Das Objekt subprocess.CompletedProcess enthält Details zum Exitcode des externen Programms und seiner Ausgabe. capture_output=True sorgt dafür, dass result.stdout und result.stderr mit der entsprechenden Ausgabe aus dem externen Programm gefüllt werden. Standardmäßig sind result.stdout und result.stderr als Bytes gebunden; das Schlüsselwortargument text=True weist Python an, die Bytes in Zeichenfolgen zu decodieren.

Im Ausgabebereich lautet stdout ocean (plus der nachfolgenden neuen Zeile, die print implizit hinzufügt); wir verfügen über kein stderr.

Versuchen wir es mit einem Beispiel, das für stderr einen nicht leeren Wert erstellt:

import subprocess
import sys

result = subprocess.run(
    [sys.executable, "-c", "raise ValueError('oops')"], capture_output=True, text=True
)
print("stdout:", result.stdout)
print("stderr:", result.stderr)

Wenn wir diesen Code ausführen, erhalten wir eine Ausgabe wie die folgende:

Output
stdout: stderr: Traceback (most recent call last): File "<string>", line 1, in <module> ValueError: oops

Dieser Code führt einen Python-Unterprozess aus, der sofort einen ValueError auslöst. Wenn wir das endgültige Ergebnis prüfen, sehen wir in stdout nichts und ein Traceback unseres ValueError in stderr. Das liegt daran, dass Python das Traceback der nicht behandelten Ausnahme in stderr schreibt.

Auslösen einer Ausnahme bei einem fehlerhaften Exitcode

Manchmal ist es nützlich, eine Ausnahme auszulösen, wenn ein ausgeführtes Programm mit einem fehlerhaften Exitcode beendet wird. Programme, die mit einem Nullcode beendet werden, werden als erfolgreich betrachtet; Programme, die mit einem Nicht-Nullcode beendet werden, werden hingegen als fehlerhaft betrachtet. Als Beispiel kann dieses Muster nützlich sein, wenn wir eine Ausnahme auslösen möchten für den Fall, dass wir git ls-files in einem Verzeichnis ausführen, das in Wahrheit kein git-Repository ist.

Wir können das Schlüsselwortargument check=True nutzen, damit für subprocess.run eine Ausnahme ausgelöst wird, wenn das externe Programm einen Nicht-Null-Exitcode zurückgibt:

import subprocess
import sys

result = subprocess.run([sys.executable, "-c", "raise ValueError('oops')"], check=True)

Wenn wir diesen Code ausführen, erhalten wir eine Ausgabe wie die folgende:

Output
Traceback (most recent call last): File "<string>", line 1, in <module> ValueError: oops Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/python3.8/subprocess.py", line 512, in run raise CalledProcessError(retcode, process.args, subprocess.CalledProcessError: Command '['/usr/local/bin/python', '-c', "raise ValueError('oops')"]' returned non-zero exit status 1.

Diese Ausgabe zeigt, dass wir einen Unterprozess ausgeführt haben, der einen Fehler ausgelöst hat, der in unserem Terminal in stderr ausgegeben wird. Dann hat subprocess.run in unserem Namen in unserem zentralen Python-Programm ordnungsgemäß einen subprocess.CalledProcessError ausgelöst.

Alternativ enthält das subprocess-Modul auch die Methode subprocess.CompletedProcess.check_returncode, die wir mit ähnlicher Wirkung aufrufen können:

import subprocess
import sys

result = subprocess.run([sys.executable, "-c", "raise ValueError('oops')"])
result.check_returncode()

Wenn wir diesen Code ausführen, erhalten wir:

Output
Traceback (most recent call last): File "<string>", line 1, in <module> ValueError: oops Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/python3.8/subprocess.py", line 444, in check_returncode raise CalledProcessError(self.returncode, self.args, self.stdout, subprocess.CalledProcessError: Command '['/usr/local/bin/python', '-c', "raise ValueError('oops')"]' returned non-zero exit status 1.

Da wir check=True nicht an subprocess.run übergeben haben, haben wir eine subprocess.CompletedProcess-Instanz erfolgreich an result gebunden, obwohl unser Programm mit einem Nicht-Nullcode beendet wurde. Ein Aufruf von result.check_returncode() löst jedoch einen subprocess.CalledProcessError aus, da erkannt wird, dass der abgeschlossene Prozess mit einem fehlerhaften Code beendet wurde.

Verwenden von Timeouts zum frühzeitigen Beenden von Programmen

subprocess.run enthält das timeout-Argument, sodass Sie ein externes Programm anhalten können, wenn dessen Ausführung zu lange dauert:

import subprocess
import sys

result = subprocess.run([sys.executable, "-c", "import time; time.sleep(2)"], timeout=1)

Wenn wir diesen Code ausführen, erhalten wir eine Ausgabe wie die folgende:

Output
Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/python3.8/subprocess.py", line 491, in run stdout, stderr = process.communicate(input, timeout=timeout) File "/usr/local/lib/python3.8/subprocess.py", line 1024, in communicate stdout, stderr = self._communicate(input, endtime, timeout) File "/usr/local/lib/python3.8/subprocess.py", line 1892, in _communicate self.wait(timeout=self._remaining_time(endtime)) File "/usr/local/lib/python3.8/subprocess.py", line 1079, in wait return self._wait(timeout=timeout) File "/usr/local/lib/python3.8/subprocess.py", line 1796, in _wait raise TimeoutExpired(self.args, timeout) subprocess.TimeoutExpired: Command '['/usr/local/bin/python', '-c', 'import time; time.sleep(2)']' timed out after 0.9997982999999522 seconds

Der Unterprozess, den wir ausführen wollten, verwendet die Funktion time.sleep, um für 2 Sekunden zu schlafen. Wir haben jedoch das Schlüsselwortargument timeout=1 an subprocess.run übergeben, um bei unserem Unterprozess nach 1 Sekunde für ein Timeout zu sorgen. Das erklärt, warum unser Aufruf an subprocess.run letztlich eine subprocess.TimeoutExpired-Ausnahme ausgelöst hat.

Beachten Sie, dass das Schlüsselwortargument timeout für subprocess.run ungefähr ist. Python wird sich bemühen, den Unterprozess nach der timeout-Zahl von Sekunden zu beenden; der Vorgang wird jedoch nicht unbedingt genau sein.

Übergeben von Eingaben an Programme

Manchmal erwarten Programme, dass Eingaben über stdin an sie übergeben werden.

Das Schlüsselwortargument input an subprocess.run ermöglicht Ihnen, Daten an stdin des Unterprozesses zu übergeben. Beispiel:

import subprocess
import sys

result = subprocess.run(
    [sys.executable, "-c", "import sys; print(sys.stdin.read())"], input=b"underwater"
)

Nach Ausführung dieses Codes erhalten wir eine Ausgabe wie die folgende:

Output
underwater

In diesem Fall haben wir die Bytes underwater an input übergeben. Unser Zielunterprozess hat sys.stdin verwendet, um das übergebene stdin (underwater) zu lesen und in unserer Ausgabe auszugeben.

Das Schlüsselwortargument input kann nützlich sein, wenn Sie mehrere subprocess.run-Aufrufe verketten möchten, um die Ausgabe eines Programms als Eingabe an ein anderes zu übergeben.

Zusammenfassung

Das subprocess-Modul ist ein leistungsfähiger Bestandeil der Python-Standardbibliothek, mit dem Sie externe Programme ausführen und deren Ausgaben bequem überprüfen können. In diesem Tutorial haben Sie gelernt, wie Sie subprocess.run verwenden können, um externe Programme zu steuern, Eingaben an sie zu übergeben, ihre Ausgabe zu analysieren und ihre Rückgabecodes zu überprüfen.

Das subprocess-Modul macht zusätzliche Klassen und Dienstprogramme verfügbar, auf die wir in diesem Tutorial nicht eingegangen sind. Nachdem Sie nun über Grundkenntnisse verfügen, können Sie die Dokumentation des subprocess-Moduls nutzen, um mehr über andere verfügbare Klassen und Dienstprogramme zu erfahren.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about us


About the authors
Default avatar

Author of Intuitive Python

Check out Intuitive Python: Productive Development for Projects that Last

https://pragprog.com/titles/dmpython/intuitive-python/



Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
Leave a comment


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more
DigitalOcean Cloud Control Panel