Wie führe ich Bash-Skripte mit Python aus?

Anwender von Linux schätzen in der Regel die Effizienz von Shell-Befehlen.

Wer mit Python arbeitet, hat möglicherweise schon versucht, Prozesse zu automatisieren, um Zeit zu sparen. Mancher nutzt dafür auch Bash-Skripte.

Python bietet jedoch eine benutzerfreundlichere Alternative für die Skripterstellung im Vergleich zu Bash. Zudem gestaltet sich die Wartung von Python-Skripten einfacher als die von Bash-Skripten, welche mit zunehmender Komplexität schwer zu handhaben sein können.

Was aber, wenn Sie bereits existierende Bash-Skripte haben, die Sie nun aus Python heraus anstoßen möchten?

Gibt es eine Möglichkeit, Bash-Befehle und Skripte innerhalb von Python auszuführen?

Ja, das Python-Modul `subprocess` ermöglicht die Ausführung von Befehlen und Skripten innerhalb von Python-Programmen. Im Folgenden erfahren Sie detailliert, wie Sie Bash-Befehle und Skripte in Ihren Python-Anwendungen nutzen können.

Bash-Befehle ausführen

Das Modul `subprocess` dient, wie bereits angedeutet, der Ausführung von Bash-Befehlen und Skripten. Es stellt verschiedene Methoden und Klassen hierfür bereit.

Zwei zentrale Elemente aus dem `subprocess`-Modul sind die Methode `run()` und die Klasse `Popen()`. Beide ermöglichen die Ausführung von Bash-Befehlen in Python. Betrachten wir diese genauer.

`subprocess.run()`

Die Methode `subprocess.run()` benötigt als Argument eine Liste von Strings. Dieses obligatorische Argument enthält den auszuführenden Bash-Befehl und seine Parameter. Das erste Listenelement ist dabei der Befehlsname, gefolgt von den Argumenten.

Ein kurzes Beispiel zur Veranschaulichung:

import subprocess
subprocess.run(["ls"])

Das obige Skript listet den Inhalt des aktuellen Arbeitsverzeichnisses auf. Es beinhaltet keine Argumente für den `ls`-Befehl. Der Befehl wird einfach übergeben. Wir können dem `ls`-Befehl jedoch auch Optionen wie `-l`, `-a`, oder `-la` hinzufügen.

Ein weiteres Beispiel mit Befehlsargumenten:

import subprocess
subprocess.run(["ls", "-la"])

Dieser Befehl zeigt alle Dateien und Verzeichnisse, inklusive versteckter Dateien, zusammen mit ihren Berechtigungen an. Das Argument `la` liefert hierbei die zusätzlichen Informationen.

Es kann beim Erstellen von Befehlen zu Fehlern kommen. Diese werden entsprechend ausgeworfen. Wenn Sie diese Fehler jedoch abfangen und später verwenden möchten, können Sie das Schlüsselwortargument `stderr` nutzen.

Hier ein Beispiel:

import subprocess
result = subprocess.run(["cat", "sample.txt"], stderr=subprocess.PIPE, text=True)
print(result.stderr)

Beachten Sie, dass die Datei `sample.txt` im aktuellen Arbeitsverzeichnis nicht vorhanden sein darf, damit ein Fehler generiert wird. Der Wert für `stderr` ist hier `PIPE`, was den Fehler in einem Objekt zurückgibt. Darauf können Sie dann über `result.stderr` zugreifen. Das Schlüsselwort `text` sorgt dafür, dass die Ausgabe als String interpretiert wird.

Ähnlich können wir die normale Befehlsausgabe über das Schlüsselwortargument `stdout` erfassen:

import subprocess
result = subprocess.run(["echo", "Hallo, Welt!"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
print(result.stdout)

`subprocess.run()` – Eingabe

Befehlen können Eingaben über das Schlüsselwortargument `input` gegeben werden. Die Eingabe erfolgt in Form eines Strings. Achten Sie darauf, `text` auf `True` zu setzen, da standardmäßig die Eingabe als Bytes interpretiert wird.

Ein Beispiel:

import subprocess
subprocess.run(["python3", "add.py"], text=True, input="2 3")

In diesem Beispiel erwartet das Python-Skript `add.py` zwei Zahlen als Eingabe. Diese werden mit dem Argument `input` übergeben.

`subprocess.Popen()`

Die Klasse `subprocess.Popen()` ist komplexer als die Methode `subprocess.run()`. Sie bietet mehr Optionen für die Befehlsausführung. Wir erstellen eine Instanz von `subprocess.Popen()` und können so den Status der Befehlsausführung überwachen, Ausgaben erfassen, Eingaben geben usw.

Es gibt mehrere Methoden der Klasse `subprocess.Popen()`, die wichtig sind. Sehen wir uns diese nacheinander mit Codebeispielen an.

`wait()`

Die Methode `wait()` hält das Python-Skript so lange an, bis die Ausführung des Befehls abgeschlossen ist. Die nachfolgenden Zeilen werden erst nach Beendigung des Befehls ausgeführt. Hier ein Beispiel:

import subprocess
process = subprocess.Popen(["ls", "-la"])
print("Fertiggestellt!")

Wenn Sie dieses Skript ausführen, werden Sie feststellen, dass die Meldung „Fertiggestellt!“ vor der Ausführung des Befehls angezeigt wird. Mit der Methode `wait()` können wir das ändern. Wir warten einfach auf die Fertigstellung des Befehls:

import subprocess
process = subprocess.Popen(["ls", "-la"])
process.wait()

print("Fertiggestellt!")

Nun wird die Meldung erst nach der Ausführung des Befehls ausgegeben, wie gewünscht.

`communicate()`

Die Methode `communicate()` gibt Ausgaben und Fehler zurück und ermöglicht die Eingabe für den Befehl. Sie gibt ein Tupel mit der Ausgabe und dem Fehler zurück. Ein Beispiel:

import subprocess
process = subprocess.Popen(["echo", "Hallo, Welt!"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
result = process.communicate()
print(result)

`subprocess.Popen()` – Eingabe

Die Eingabe an `Popen` wird nicht direkt übergeben. Wir benötigen das Schlüsselwortargument `stdin`, um die Eingabe für den Befehl zu definieren. Die Instanz von `Popen` liefert ein `stdin`-Objekt, das eine Methode `write` besitzt, mit der wir die Eingabe übergeben.

Standardmäßig werden Eingaben als Byte-ähnliche Objekte akzeptiert. Vergessen Sie daher nicht, das Schlüsselwortargument `text` beim Erstellen der `Popen`-Instanz auf `True` zu setzen.

Hier ein Beispiel:

import subprocess
process = subprocess.Popen(["python3", "add.py"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
process.stdin.write("2 3")
process.stdin.close()
print(process.stdout.read())

`poll()`

Die Methode `poll()` überprüft, ob die Befehlsausführung abgeschlossen ist. Die Methode gibt `None` zurück, solange der Befehl noch ausgeführt wird. Betrachten wir folgendes Beispiel:

import subprocess
process = subprocess.Popen(['ping', '-c 5', 'geekflare.com'], stdout=subprocess.PIPE, text=True)
while True:
    output = process.stdout.readline()
    if output:
    	print(output.strip())
    result = process.poll()
    if result is not None:
        break

Hier führen wir den Ping-Befehl mit 5 Anfragen aus. Eine Endlosschleife iteriert, bis der Befehl abgeschlossen ist. Die Methode `poll()` überprüft den Status der Befehlsausführung. Wenn `poll()` einen anderen Wert als `None` zurückgibt, ist die Ausführung abgeschlossen und die Endlosschleife wird beendet.

Bash-Skripte ausführen

Wir haben nun zwei Wege kennengelernt, um Befehle auszuführen. Betrachten wir nun die Ausführung von Bash-Skripten in Python.

Das Modul `subprocess` bietet die Methode `call()`, um Bash-Skripte auszuführen. Diese Methode gibt den Exit-Code des Bash-Skripts zurück. Der Standard-Exit-Code ist `0`. Hier ein Beispiel:

Erstellen Sie ein Bash-Skript mit dem Namen `practice.sh`:

#!/bin/bash

echo "Hallo, Welt!"
exit 1

Erstellen Sie nun ein Python-Skript, das obiges Bash-Skript ausführt:

import subprocess
exit_code = subprocess.call('./practice.sh')
print(exit_code)

Nach Ausführung des Python-Skripts erhalten Sie folgende Ausgabe:

Hallo, Welt!
1

Fazit

Wir haben gezeigt, wie Sie Bash-Befehle und -Skripte innerhalb von Python ausführen können. Dies ermöglicht eine effizientere Automatisierung von Aufgaben.

Viel Spaß beim Programmieren! 👨‍💻