One place for hosting & domains

      externer

      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.



      Source link