Der Autor hat den COVID-19 Relief Fund dazu ausgewählt, eine Spende im Rahmen des Programms Write for DOnations zu erhalten.
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.
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:
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:
Outputocean
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.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.-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.
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:
Outputstdout: 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:
Outputstdout:
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.
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:
OutputTraceback (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:
OutputTraceback (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.
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:
OutputTraceback (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.
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:
Outputunderwater
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.
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.
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!