Unterprozesse ermöglichen eine Interaktion mit dem Betriebssystem auf einer ganz neuen Ebene.
Unser Computer führt ständig Unterprozesse aus. Tatsächlich werden allein beim Lesen dieses Artikels zahlreiche Prozesse, wie beispielsweise ein Netzwerkmanager oder der Webbrowser, gestartet.
Das Faszinierende daran ist, dass jede Aktion, die wir auf unserem Computer ausführen, das Aufrufen eines Unterprozesses beinhaltet. Dies gilt sogar, wenn wir ein einfaches „Hallo Welt“-Skript in Python schreiben.
Das Konzept des Unterprozesses mag selbst erfahrenen Programmierern zunächst unklar erscheinen. Dieser Artikel befasst sich eingehend mit dem Grundprinzip des Unterprozesses und der Verwendung der Python Standardbibliothek für Unterprozesse.
Nach Durcharbeiten dieses Tutorials werden Sie:
- Das Konzept des Unterprozesses verstehen
- Die Grundlagen der Python-Unterprozessbibliothek erlernt haben
- Ihre Python-Kenntnisse anhand praktischer Beispiele erweitern
Lassen Sie uns eintauchen.
Das Konzept des Unterprozesses
Im Allgemeinen ist ein Unterprozess ein Computerprozess, der von einem anderen Prozess erstellt wurde.
Wir können uns einen Unterprozess wie einen Baum vorstellen, bei dem jeder übergeordnete Prozess untergeordnete Prozesse ausführt. Ich weiß, das kann etwas verwirrend sein, aber betrachten wir es mit einer einfachen Grafik.
Es gibt verschiedene Möglichkeiten, den auf unserem Computer laufenden Prozess zu visualisieren. In UNIX-Systemen (Linux & MAC) gibt es beispielsweise htop, einen interaktiven Prozessbetrachter.
Der Baummodus ist ein nützliches Werkzeug, um sich die laufenden Unterprozesse anzusehen. Er kann mit F5 aktiviert werden.
Wenn wir uns den Befehlsbereich genauer ansehen, können wir die Struktur der auf unserem Computer ausgeführten Prozesse erkennen.
Alles beginnt mit /sbin/init. Dies ist der Befehl, der jeden Prozess auf unserem Computer startet. Von hier aus können wir den Beginn anderer Prozesse wie xfce4-screenshoter und xfce4-terminal sehen (was zu noch mehr Unterprozessen führt).
Unter Windows haben wir den bekannten Task-Manager, der nützlich ist, um Programme zu beenden, die auf unserem Computer nicht mehr reagieren.
Nun haben wir ein klares Konzept. Sehen wir uns an, wie wir Unterprozesse in Python implementieren können.
Unterprozesse in Python
Ein Unterprozess in Python ist eine Aufgabe, die ein Python-Skript an das Betriebssystem (OS) delegiert.
Die Unterprozessbibliothek ermöglicht es uns, Unterprozesse direkt von Python aus auszuführen und zu verwalten. Dazu gehört die Arbeit mit der Standardeingabe (stdin), der Standardausgabe (stdout) und Rückgabecodes.
Wir müssen sie nicht mit PIP installieren, da sie Teil der Python Standardbibliothek ist.
Daher können wir mit der Verwendung von Unterprozessen in Python beginnen, indem wir einfach das Modul importieren.
import subprocess # Verwendung des Moduls ...
Hinweis: Um diesem Artikel folgen zu können, sollten Sie Python 3.5 oder höher verwenden.
Um die aktuelle Python-Version zu überprüfen, führen Sie einfach Folgendes aus:
❯ python --version Python 3.9.5 # Mein Ergebnis
Falls die Python-Version, die Sie erhalten, 2.x ist, können Sie den folgenden Befehl verwenden:
python3 --version
Um mit dem Thema fortzufahren: Die Hauptidee der Unterprozessbibliothek ist die Interaktion mit dem Betriebssystem, indem alle gewünschten Befehle direkt vom Python-Interpreter aus ausgeführt werden.
Das bedeutet, dass wir alles tun können, was unser Betriebssystem zulässt (und solange Sie Ihr Root-Dateisystem nicht entfernen 😅).
Lassen Sie uns sehen, wie es verwendet wird, indem wir ein einfaches Skript erstellen, das die Dateien des aktuellen Verzeichnisses auflistet.
Erste Unterprozessanwendung
Zuerst erstellen wir eine Datei mit dem Namen list_dir.py. Hier werden wir mit dem Auflisten von Dateien experimentieren.
touch list_dir.py
Öffnen wir nun diese Datei und verwenden den folgenden Code.
import subprocess subprocess.run('ls')
Zuerst importieren wir das Unterprozessmodul und verwenden dann die Funktion run, die den als Argument übergebenen Befehl ausführt.
Diese Funktion wurde in Python 3.5 als komfortable Abkürzung für subprocess.Popen eingeführt. Die Funktion subprocess.run ermöglicht es uns, einen Befehl auszuführen und auf dessen Beendigung zu warten, im Gegensatz zu Popen, wo wir die Option haben, die Kommunikation später aufzurufen.
Was die Codeausgabe betrifft: „ls“ ist ein UNIX-Befehl, der die Dateien des Verzeichnisses auflistet, in dem Sie sich befinden. Wenn Sie diesen Befehl ausführen, erhalten Sie daher eine Liste der Dateien, die im aktuellen Verzeichnis vorhanden sind.
❯ python list_dir.py example.py LICENSE list_dir.py README.md
Hinweis: Beachten Sie, dass Sie unter Windows andere Befehle verwenden müssen. Anstelle von „ls“ können Sie beispielsweise „dir“ verwenden.
Das mag zu einfach erscheinen, und Sie haben Recht. Sie möchten die ganze Kraft der Shell nutzen. Lassen Sie uns also lernen, wie man Argumente mit subprocess an die Shell übergibt.
Um beispielsweise auch die versteckten Dateien aufzulisten (die mit einem Punkt beginnen) und auch alle Metadaten der Dateien aufzulisten, schreiben wir den folgenden Code.
import subprocess # subprocess.run('ls') # Einfacher Befehl subprocess.run('ls -la', shell=True)
Wir führen diesen Befehl als Zeichenkette aus und verwenden das Argument Shell. Das bedeutet, dass wir zu Beginn der Ausführung unseres Unterprozesses eine Shell aufrufen und das Befehlsargument direkt von der Shell interpretiert wird.
Die Verwendung von shell=True hat jedoch viele Nachteile, und das Schlimmste sind die möglichen Sicherheitslücken. Sie können mehr darüber in der offiziellen Dokumentation lesen.
Der beste Weg, Befehle an die run-Funktion zu übergeben, ist die Verwendung einer Liste, bei der lst[0] der Aufrufbefehl ist (in diesem Fall ls) und lst[n] die Argumente dieses Befehls sind.
Wenn wir das tun, sieht unser Code so aus:
import subprocess # subprocess.run('ls') # Einfacher Befehl # subprocess.run('ls -la', shell=True) # Gefährlicher Befehl subprocess.run(['ls', '-la'])
Wenn wir die Standardausgabe eines Unterprozesses in einer Variablen speichern möchten, können wir dies tun, indem wir das Argument capture_output auf true setzen.
list_of_files = subprocess.run(['ls', '-la'], capture_output=True) print(list_of_files.stdout) ❯ python list_dir.py b'total 36ndrwxr-xr-x 3 daniel daniel 4096 may 20 21:08 .ndrwx------ 30 daniel daniel 4096 may 20 18:03 ..n-rw-r--r-- 1 daniel daniel 55 may 20 20:18 example.pyndrwxr-xr-x 8 daniel daniel 4096 may 20 17:31 .gitn-rw-r--r-- 1 daniel daniel 2160 may 17 22:23 .gitignoren-rw-r--r-- 1 daniel daniel 271 may 20 19:53 internet_checker.pyn-rw-r--r-- 1 daniel daniel 1076 may 17 22:23 LICENSEn-rw-r--r-- 1 daniel daniel 216 may 20 22:12 list_dir.pyn-rw-r--r-- 1 daniel daniel 22 may 17 22:23 README.mdn'
Um auf die Ausgabe eines Prozesses zuzugreifen, verwenden wir das Instanzattribut stdout.
In diesem Fall möchten wir die Ausgabe als Zeichenkette anstelle von Bytes speichern, und wir können dies tun, indem wir das Textargument auf wahr setzen.
list_of_files = subprocess.run(['ls', '-la'], capture_output=True, text=True) print(list_of_files.stdout) ❯ python list_dir.py total 36 drwxr-xr-x 3 daniel daniel 4096 may 20 21:08 . drwx------ 30 daniel daniel 4096 may 20 18:03 .. -rw-r--r-- 1 daniel daniel 55 may 20 20:18 example.py drwxr-xr-x 8 daniel daniel 4096 may 20 17:31 .git -rw-r--r-- 1 daniel daniel 2160 may 17 22:23 .gitignore -rw-r--r-- 1 daniel daniel 271 may 20 19:53 internet_checker.py -rw-r--r-- 1 daniel daniel 1076 may 17 22:23 LICENSE -rw-r--r-- 1 daniel daniel 227 may 20 22:14 list_dir.py -rw-r--r-- 1 daniel daniel 22 may 17 22:23 README.md
Perfekt, jetzt, da wir die Grundlagen der Subprozessbibliothek kennen, ist es an der Zeit, mit einigen Anwendungsbeispielen fortzufahren.
Anwendungsbeispiele für Unterprozesse in Python
In diesem Abschnitt werden wir einige praktische Anwendungen der Unterprozessbibliothek besprechen. Sie können sie alle in diesem Github-Repository finden.
Programmprüfer
Eine der Hauptanwendungen dieser Bibliothek ist die Möglichkeit, einfache Betriebssystemoperationen durchzuführen.
Beispielsweise ein einfaches Skript, das überprüft, ob ein Programm installiert ist. Unter Linux können wir dies mit dem Befehl „which“ tun.
'''Programmprüfer mit Unterprozess''' import subprocess program = 'git' process = subprocess.run(['which', program], capture_output=True, text=True) if process.returncode == 0: print(f'Das Programm "{program}" ist installiert') print(f'Der Speicherort der Binärdatei ist: {process.stdout}') else: print(f'Leider ist das Programm {program} nicht installiert') print(process.stderr)
Hinweis: Unter UNIX ist der Statuscode eines erfolgreichen Befehls 0. Andernfalls ist während der Ausführung etwas schiefgelaufen.
Da wir das Argument shell=True nicht verwenden, können wir die Benutzereingabe sicher entgegennehmen. Außerdem können wir überprüfen, ob die Eingabe ein gültiges Programm mit einem Regex-Muster ist.
import subprocess import re programs = input('Trennen Sie die Programme durch ein Leerzeichen: ').split() secure_pattern = '^[a-zA-Z0-9]+$' for program in programs: if not re.match(secure_pattern, program): print("Leider können wir dieses Programm nicht prüfen") continue process = subprocess.run( ['which', program], capture_output=True, text=True) if process.returncode == 0: print(f'Das Programm "{program}" ist installiert') print(f'Der Speicherort der Binärdatei ist: {process.stdout}') else: print(f'Leider ist das Programm {program} nicht installiert') print(process.stderr) print('n')
In diesem Fall erhalten wir die Programme vom Benutzer und verwenden einen Regex-Ausdruck, der bestätigt, dass die Programmzeichenfolge nur Buchstaben und Ziffern enthält. Wir überprüfen die Existenz jedes Programms mit einer for-Schleife.
Einfaches Grep in Python
Ihr Freund Tom hat eine Liste von Mustern in einer Textdatei und eine weitere große Datei, in der er die Anzahl der Übereinstimmungen für jedes Muster abrufen möchte. Er hat Stunden damit verbracht, den Befehl grep für jedes Muster auszuführen.
Glücklicherweise wissen Sie, wie Sie dieses Problem mit Python lösen können, und Sie werden ihm helfen, diese Aufgabe in wenigen Sekunden zu erledigen.
import subprocess patterns_file="patterns.txt" readfile="romeo-full.txt" with open(patterns_file, 'r') as f: for pattern in f: pattern = pattern.strip() process = subprocess.run( ['grep', '-c', f'{pattern}', readfile], capture_output=True, text=True) if int(process.stdout) == 0: print( f'Das Muster "{pattern}" stimmte mit keiner Zeile von {readfile} überein') continue print(f'Das Muster "{pattern}" stimmte {process.stdout.strip()} Mal überein')
Wenn wir uns diese Datei ansehen, definieren wir zwei Variablen, die die Dateinamen sind, mit denen wir arbeiten möchten. Dann öffnen wir die Datei, die alle Muster enthält, und durchlaufen sie. Als Nächstes rufen wir einen Unterprozess auf, der einen grep-Befehl mit dem Flag „-c“ (Anzahl) ausführt, und bestimmen die Ausgabe der Übereinstimmung mit einer Bedingung.
Wenn Sie diese Datei ausführen (denken Sie daran, dass Sie die Textdateien aus dem Github-Repository herunterladen müssen).
Einrichten einer virtuellen Umgebung mit Unterprozess
Eines der coolsten Dinge, die Sie mit Python tun können, ist die Prozessautomatisierung. Diese Art von Skript kann Ihnen jede Woche Stunden an Zeit sparen.
Wir erstellen zum Beispiel ein Setup-Skript, das eine virtuelle Umgebung für uns erstellt und versucht, eine requirements.txt-Datei im aktuellen Verzeichnis zu finden, um alle Abhängigkeiten zu installieren.
import subprocess from pathlib import Path VENV_NAME = '.venv' REQUIREMENTS = 'requirements.txt' process1 = subprocess.run(['which', 'python3'], capture_output=True, text=True) if process1.returncode != 0: raise OSError('Leider ist python3 nicht installiert') python_bin = process1.stdout.strip() print(f'Python gefunden in: {python_bin}') process2 = subprocess.run('echo "$SHELL"', shell=True, capture_output=True, text=True) shell_bin = process2.stdout.split('/')[-1] create_venv = subprocess.run([python_bin, '-m', 'venv', VENV_NAME], check=True) if create_venv.returncode == 0: print(f'Ihre venv {VENV_NAME} wurde erstellt') pip_bin = f'{VENV_NAME}/bin/pip3' if Path(REQUIREMENTS).exists(): print(f'Anforderungsdatei "{REQUIREMENTS}" gefunden') print('Installiere Anforderungen') subprocess.run([pip_bin, 'install', '-r', REQUIREMENTS]) print('Prozess abgeschlossen! Aktivieren Sie nun Ihre Umgebung mit "source .venv/bin/activate"') else: print("Keine Anforderungen angegeben ...")
In diesem Fall verwenden wir mehrere Prozesse und analysieren die Daten, die wir in unserem Python-Skript benötigen. Wir verwenden auch die pathlib-Bibliothek, um herauszufinden, ob die Datei requirements.txt existiert.
Wenn Sie die Python-Datei ausführen, erhalten Sie einige nützliche Meldungen darüber, was mit dem Betriebssystem geschieht.
❯ python setup.py Python gefunden in: /usr/bin/python3 Ihre venv .venv wurde erstellt Anforderungsdatei "requirements.txt" gefunden Installiere Anforderungen Collecting asgiref==3.3.4 ....... Prozess abgeschlossen! Aktivieren Sie nun Ihre Umgebung mit "source .venv/bin/activate"
Beachten Sie, dass wir die Ausgabe des Installationsprozesses erhalten, da wir die Standardausgabe nicht in eine Variable umleiten.
Ausführen einer anderen Programmiersprache
Wir können andere Programmiersprachen mit Python ausführen und die Ausgabe dieser Dateien abrufen. Dies ist möglich, weil die Teilprozesse direkt mit dem Betriebssystem interagieren.
Erstellen wir zum Beispiel ein Hallo-Welt-Programm in C++ und Java. Um die folgende Datei auszuführen, müssen Sie die C++– und Java-Compiler installieren.
hallowelt.cpp
#include <iostream> int main(){ std::cout << "Dies ist ein Hallo Welt in C++" << std::endl; return 0; }
hallowelt.java
class HelloWorld{ public static void main(String args[]){ System.out.println("Dies ist ein Hallo Welt in Java"); } }
Ich weiß, dass dies im Vergleich zu einem einfachen Python-Einzeiler viel Code zu sein scheint, aber dies dient nur zu Testzwecken.
Wir erstellen ein Python-Skript, das alle C++- und Java-Dateien in einem Verzeichnis ausführt. Dazu möchten wir zuerst eine Liste von Dateien erhalten, die von der Dateierweiterung abhängen, und glob ermöglicht es uns, dies auf einfache Weise zu tun!
from glob import glob # Ruft Dateien mit jeder Erweiterung ab java_files = glob('*.java') cpp_files = glob('*.cpp')
Danach können wir mit der Verwendung von Unterprozessen beginnen, um jeden Dateityp auszuführen.
for file in cpp_files: process = subprocess.run(f'g++ {file} -o out; ./out', shell=True, capture_output=True, text=True) output = process.stdout.strip() + ' BTW dies wurde von Python ausgeführt' print(output) for file in java_files: without_ext = file.strip('.java') process = subprocess.run(f'java {file}; java {without_ext}',shell=True, capture_output=True, text=True) output = process.stdout.strip() + ' Ein Python-Unterprozess hat das ausgeführt :)' print(output)
Ein kleiner Trick besteht darin, die String-Funktionsstreifen zu verwenden, um die Ausgabe zu ändern und nur das zu erhalten, was wir brauchen.
Hinweis: Seien Sie vorsichtig bei der Ausführung großer Java- oder C++-Dateien, da wir ihre Ausgabe in den Speicher laden und dies zu einem Speicherleck führen könnte.
Öffnen externer Programme
Wir können andere Programme ausführen, indem wir einfach deren Speicherort für Binärdateien über einen Unterprozess aufrufen.
Probieren wir es aus, indem wir Brave öffnen, meinen bevorzugten Webbrowser.
import subprocess subprocess.run('brave')
Dies öffnet eine Browserinstanz oder nur eine weitere Registerkarte, wenn Sie den Browser bereits ausführen.
Wie bei jedem anderen Programm, das Flags akzeptiert, können wir diese verwenden, um das gewünschte Verhalten zu erzielen.
import subprocess subprocess.run(['brave', '--incognito'])
Zusammenfassend
Ein Unterprozess ist ein Computerprozess, der von einem anderen Prozess erstellt wird. Wir können die Prozesse, die auf unserem Computer laufen, mit Tools wie htop und dem Task-Manager überprüfen.
Python hat eine eigene Bibliothek für die Arbeit mit Unterprozessen. Derzeit bietet uns die Run-Funktion eine einfache Schnittstelle zum Erstellen und Verwalten von Unterprozessen.
Wir können damit jede Art von Anwendung erstellen, da wir direkt mit dem Betriebssystem interagieren.
Denken Sie schließlich daran, dass der beste Weg zu lernen darin besteht, etwas zu erschaffen, das Sie auch verwenden wollen.