Python subprocess Modul: Externe Befehle und Automatisierung in Python einfach umsetzen

1. Was ist das Python-Subprocess-Modul?

Überblick

Das Python-subprocess-Modul ist ein leistungsfähiges Werkzeug, um Systembefehle oder externe Programme direkt aus Python heraus auszuführen. Mit diesem Modul können Sie die Standard-Ein-/Ausgabe sowie Prozesse verwalten und so die Zusammenarbeit zwischen Python-Skripten und externen Programmen erheblich vereinfachen. Es bietet eine sicherere und flexiblere Prozesssteuerung als die herkömmlichen Methoden wie os.system() oder das commands-Modul.

Hauptanwendungsbereiche

  • Ausführen von Shell-Befehlen: Einfache Ausführung von Systemkommandos.
  • Prozessverwaltung: Starten externer Programme und Umleitung von Standard-Ein-/Ausgabe.
  • Asynchrone Verarbeitung: Verwaltung von lang laufenden oder parallelen Aufgaben.

2. Grundlegende Verwendung: subprocess.run()

Grundlegende Nutzung

Mit subprocess.run() können Sie Systembefehle einfach aus Python heraus ausführen. Möchten Sie beispielsweise alle Dateien in einem Verzeichnis auflisten, verwenden Sie folgenden Code:

import subprocess

result = subprocess.run(['ls', '-l'], capture_output=True, text=True)
print(result.stdout)

Dieser Code führt den Befehl ls -l aus, speichert die Ausgabe in stdout und verarbeitet sie in Python. Mit capture_output=True wird die Standardausgabe abgefangen und dank text=True als String behandelt.

Fehlerbehandlung

Bei Verwendung von subprocess.run() können Sie mit stderr Fehlermeldungen abfragen, falls der Befehl fehlschlägt. Mit returncode lässt sich zudem der Erfolg der Ausführung überprüfen.

result = subprocess.run(['ls', 'nonexistentfile'], capture_output=True, text=True)
if result.returncode != 0:
    print(f"Fehler: {result.stderr}")

In diesem Beispiel wird eine Fehlermeldung ausgegeben, wenn die angegebene Datei nicht existiert.

侍エンジニア塾

3. Asynchrone Ausführung: subprocess.Popen()

Asynchrone Verarbeitung mit Popen

Da subprocess.run() synchron arbeitet und das Programm bis zur Beendigung des Befehls wartet, empfiehlt sich für asynchrone Ausführung subprocess.Popen(). Damit können Prozesse parallel zu anderen Aufgaben ausgeführt werden.

import subprocess

proc = subprocess.Popen(['sleep', '5'], stdout=subprocess.PIPE)
print("Prozess wurde gestartet")
proc.wait()
print("Prozess ist beendet")

Hier wird der Befehl sleep 5 asynchron ausgeführt, sodass währenddessen weitere Aufgaben bearbeitet werden können.

Steuerung von Ein- und Ausgabe

Mit Popen lässt sich die Umleitung der Standard-Ein-/Ausgabe flexibel steuern. Im folgenden Beispiel werden Daten aus einer Datei gelesen, mit cat verarbeitet und in eine andere Datei geschrieben:

with open('input.txt', 'r') as infile, open('output.txt', 'w') as outfile:
    proc = subprocess.Popen(['cat'], stdin=infile, stdout=outfile)
    proc.wait()

So können Ein- und Ausgaben externer Befehle gezielt in Dateien umgeleitet werden.

4. Anwendungsbeispiel: Automatisierungsskripte

Datei-Backup

Das subprocess-Modul eignet sich hervorragend zur Automatisierung von Systemverwaltungsaufgaben. Im folgenden Beispiel werden Dateien automatisch in ein Backup-Verzeichnis kopiert:

import subprocess

files_to_backup = ['file1.txt', 'file2.txt', 'file3.txt']
backup_dir = '/backup/directory/'

for file in files_to_backup:
    subprocess.run(['cp', file, backup_dir])

Mit diesem Skript lassen sich regelmäßige Backups leicht automatisieren.

Einsatz in CI/CD-Pipelines

Auch in Continuous Integration (CI) und Continuous Delivery (CD) Pipelines findet subprocess Verwendung, z.B. um Testscripte automatisch auszuführen oder Deployments zu automatisieren. So kann z.B. nach erfolgreichem Test automatisch der nächste Schritt eingeleitet werden.

5. Sicherheit und Best Practices

Risiken bei shell=True

Die Option shell=True führt Befehle über die Shell aus, birgt jedoch Sicherheitsrisiken. Besonders bei externer Benutzereingabe besteht Gefahr von Shell-Injection-Angriffen. Durch Verwendung von shell=False lassen sich diese Risiken minimieren.

import subprocess

# Empfohlene (sichere) Methode
subprocess.run(['ls', '-l'])

# shell=True (mit Vorsicht verwenden)
subprocess.run('ls -l', shell=True)

Plattformübergreifende Kompatibilität

Systembefehle unterscheiden sich je nach Betriebssystem. Mit dem Python-platform-Modul kann das Skript den auszuführenden Befehl je nach OS steuern:

import platform
import subprocess

if platform.system() == "Windows":
    subprocess.run(['dir'], shell=True)
else:
    subprocess.run(['ls', '-l'])

6. Fehlersuche und Debugging

Häufige Fehler und deren Behebung

Bei der Verwendung von subprocess können Fehler wie „Datei nicht gefunden“ oder „Zugriff verweigert“ auftreten. Diese lassen sich mit stderr erfassen und über returncode auswerten.

Tipps für das Debugging

Die Option check=True löst im Fehlerfall eine Exception aus und hilft so, Probleme frühzeitig zu erkennen. Außerdem sollten Standardausgabe und Fehlermeldungen für die Analyse geloggt werden.

import subprocess

try:
    result = subprocess.run(['ls', '-l'], check=True, capture_output=True, text=True)
    print(result.stdout)
except subprocess.CalledProcessError as e:
    print(f"Ein Fehler ist aufgetreten: {e}")

7. Asynchrone Verarbeitung mit asyncio

Asynchrone Ausführung mit asyncio

Mit asyncio kann subprocess kombiniert werden, um mehrere Prozesse parallel und asynchron auszuführen. Im folgenden Beispiel wird der Befehl ls asynchron gestartet und das Ergebnis abgefragt:

import asyncio
import subprocess

async def run_command():
    proc = await asyncio.create_subprocess_exec('ls', '-l',
        stdout=asyncio.subprocess.PIPE,
        stderr=asyncio.subprocess.PIPE)

    stdout, stderr = await proc.communicate()

    if stdout:
        print(f'[stdout]n{stdout.decode()}')
    if stderr:
        print(f'[stderr]n{stderr.decode()}')

asyncio.run(run_command())

Dieses Beispiel zeigt, wie Sie mit asyncio externe Prozesse effizient und asynchron steuern können.

RUNTEQ(ランテック)|超実戦型エンジニア育成スクール