Utforskning av Underprosesser: En Dypdykk i Python
Underprosesser gir deg muligheten til å interagere med operativsystemet på en helt ny måte.
Datamaskiner kjører kontinuerlig underprosesser. Faktisk, bare det å lese denne artikkelen medfører at mange prosesser kjøres, som en nettverksbehandler eller selve nettleseren.
Det fascinerende er at enhver handling vi utfører på datamaskinen involverer initiering av en underprosess. Dette gjelder selv når vi skriver et enkelt «hei verden»-skript i Python.
Konseptet underprosesser kan virke komplekst, selv for erfarne programmerere. Denne artikkelen vil dykke dypt inn i det grunnleggende konseptet med underprosesser og forklare hvordan du bruker Pythons standardbibliotek for underprosesser.
Etter denne veiledningen vil du:
- Forstå hva en underprosess er.
- Ha kunnskap om det grunnleggende i Pythons underprosessbibliotek.
- Ha praktisert Python-ferdigheter med nyttige eksempler.
La oss begynne!
Forståelse av Underprosesser
En underprosess er, enkelt sagt, en datamaskinprosess som er startet av en annen prosess.
Vi kan visualisere en underprosess som et tre, der hver hovedprosess har underordnede prosesser under seg. Dette kan virke litt forvirrende, så la oss illustrere det med en enkel grafikk.
Det finnes forskjellige måter å visualisere prosessene som kjører på datamaskinen din. I UNIX-systemer (Linux & MAC) har vi for eksempel htop, et interaktivt verktøy for prosessvisning.
Trevisningen er spesielt nyttig for å observere de aktive underprosessene. Denne kan aktiveres med F5-tasten.
Når vi ser nærmere på kommandodelen, kan vi observere strukturen til prosessene som kjører på datamaskinen vår.
Alt starter med /sbin/init, kommandoen som starter hver prosess på datamaskinen. Derfra ser vi starten på andre prosesser som xfce4-display manager og xfce4-terminalen, som i sin tur skaper enda flere underprosesser.
I Windows har vi Oppgavebehandling, et praktisk verktøy for å avslutte programmer som har krasjet.
Nå har vi en klar forståelse av konseptet. La oss se hvordan vi kan implementere underprosesser i Python.
Underprosesser i Python
I Python er en underprosess en oppgave som et Python-skript delegerer til operativsystemet (OS).
Underprosessbiblioteket gir oss mulighet til å utføre og håndtere underprosesser direkte fra Python. Dette innebærer å jobbe med standard input (stdin), standard output (stdout) og returkoder.
Vi trenger ikke å installere det med PIP, da det er en del av Pythons standardbibliotek.
Derfor kan vi begynne å bruke underprosesser i Python ved å importere modulen.
import subprocess # Bruk modulen ....
Merk: For å følge denne artikkelen bør du ha Python 3.5 eller nyere installert.
For å sjekke hvilken Python-versjon du har, kjører du:
❯ python --version Python 3.9.5 # Mitt resultat
Hvis du har en eldre Python 2.x versjon, kan du bruke følgende kommando:
python3 --version
Hovedideen bak underprosessbiblioteket er å kunne samhandle med operativsystemet ved å utføre kommandoer direkte fra Python-tolken.
Dette betyr at vi kan gjøre det meste så lenge operativsystemet tillater det (og så lenge du ikke sletter rotfilsystemet 😅).
La oss se hvordan vi kan bruke det ved å lage et enkelt skript som viser filene i gjeldende katalog.
Første Underprosessapplikasjon
La oss først opprette en fil kalt `list_dir.py`. Dette vil være filen vi skal bruke for å eksperimentere med å liste filer.
touch list_dir.py
La oss åpne filen og bruke følgende kode:
import subprocess subprocess.run('ls')
Vi importerer først underprosessmodulen og bruker deretter `run`-funksjonen som utfører kommandoen vi sender som argument.
Denne funksjonen ble introdusert i Python 3.5, som en enklere måte å bruke `subprocess.Popen` på. `Subprocess.run`-funksjonen lar oss kjøre en kommando og vente til den er ferdig, i motsetning til `Popen`, der vi har muligheten til å kalle `communicate` senere.
Kommandoen `ls` er en UNIX-kommando som viser filene i gjeldende katalog. Hvis du kjører denne kommandoen, vil du få en liste over filer som finnes i katalogen.
❯ python list_dir.py example.py LICENSE list_dir.py README.md
Merk: I Windows må du bruke andre kommandoer. For eksempel, i stedet for `ls`, kan du bruke `dir`.
Dette kan virke for enkelt, og det er det til en viss grad. Du ønsker full tilgang til kraften som shell gir deg. La oss derfor lære hvordan du sender argumenter til shell med underprosess.
For å vise skjulte filer (de som starter med en prikk) og også all metadataen til filene, skriver vi følgende kode:
import subprocess # subprocess.run('ls') # Enkel kommando subprocess.run('ls -la', shell=True)
Vi kjører kommandoen som en streng og bruker `shell`-argumentet. Det betyr at vi kaller et shell ved starten av underprosessen, og kommandoargumentet blir tolket direkte av shell.
Men å bruke `shell=True` har flere ulemper, og den verste er mulige sikkerhetsbrudd. Du kan lese om dem i den offisielle dokumentasjonen.
Den beste måten å sende kommandoer til `run`-funksjonen er å bruke en liste, der `lst[0]` er kommandoen som skal kalles (`ls` i dette tilfellet), og `lst[n]` er argumentene for den kommandoen.
Hvis vi gjør det, vil koden vår se slik ut:
import subprocess # subprocess.run('ls') # Enkel kommando # subprocess.run('ls -la', shell=True) # Farlig kommando subprocess.run(['ls', '-la'])
Hvis vi vil lagre standard output fra en underprosess i en variabel, kan vi gjøre det ved å sette `capture_output`-argumentet til `True`.
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'
For å få tilgang til output fra en prosess, bruker vi instansattributtet `stdout`.
I dette tilfellet vil vi lagre output som en streng, i stedet for bytes, og vi kan gjøre det ved å sette `text`-argumentet til `True`.
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! Nå som vi forstår det grunnleggende i underprosessbiblioteket, er det på tide å se på noen brukstilfeller.
Brukstilfeller for Underprosesser i Python
I denne seksjonen vil vi gå gjennom noen praktiske anvendelser for underprosessbiblioteket. Du kan finne alle eksemplene i dette Github-depotet.
Programkontroll
En av de primære bruksområdene for dette biblioteket er å utføre enkle operasjoner i operativsystemet.
For eksempel et enkelt skript som sjekker om et program er installert. I Linux kan vi gjøre dette med `which`-kommandoen.
'''Programkontroll med underprosess''' import subprocess program = 'git' process = subprocess. run(['which', program], capture_output=True, text=True) if process.returncode == 0: print(f'Programmet "{program}" er installert') print(f'Plasseringen av binærfilen er: {process.stdout}') else: print(f'Beklager, {program} er ikke installert') print(process.stderr)
Merk: I UNIX er statuskoden 0 når en kommando kjøres vellykket. Ellers har noe gått galt under utførelsen.
Siden vi ikke bruker `shell=True`-argumentet, kan vi trygt hente inn brukerdata. Vi kan også sjekke om inndataene er et gyldig program med et regex-mønster.
import subprocess import re programs = input('Skill programmene med et mellomrom: ').split() secure_pattern = 'ons' for program in programs: if not re.match(secure_pattern, program): print("Beklager, vi kan ikke sjekke det programmet") continue process = subprocess. run( ['which', program], capture_output=True, text=True) if process.returncode == 0: print(f'Programmet "{program}" er installert') print(f'Plasseringen av binærfilen er: {process.stdout}') else: print(f'Beklager, {program} er ikke installert') print(process.stderr) print('n')
I dette tilfellet henter vi programmene fra brukeren og bruker et regulært uttrykk som bekrefter at programstrengen bare inneholder bokstaver og tall. Vi sjekker eksistensen av hvert program med en løkke.
Enkel Grep i Python
Din venn Tom har en liste over mønstre i en tekstfil og en annen stor fil hvor han ønsker å finne antall treff for hvert mønster. Han ville brukt timer på å kjøre `grep`-kommandoen for hvert mønster.
Heldigvis vet du hvordan du løser dette problemet med Python, og du vil hjelpe ham med å utføre denne oppgaven på få sekunder.
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'Mønsteret "{pattern}" matchet ingen linjer i {readfile}') continue print(f'Mønsteret "{pattern}" matchet {process.stdout.strip()} ganger')
I denne filen definerer vi to variabler som er filnavnene vi ønsker å arbeide med. Deretter åpner vi filen som inneholder alle mønstrene og itererer over dem. Etter det kaller vi en underprosess som kjører en grep-kommando med flagget `-c` (betyr telle) og finner resultatet av matchingen med en betinget setning.
Hvis du kjører denne filen (husk at du kan laste ned tekstfilene fra Github-repoet)
Oppsett av et Virtuelt Miljø med Underprosess
En av de kuleste tingene du kan gjøre med Python er prosessautomatisering. Denne typen skript kan spare deg for mange timer i uken.
La oss for eksempel lage et oppsettskript som skaper et virtuelt miljø for oss og prøver å finne en `requirements.txt`-fil i gjeldende katalog for å installere alle avhengighetene.
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('Beklager, python3 er ikke installert') python_bin = process1.stdout.strip() print(f'Python funnet i: {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'Ditt virtuelle miljø {VENV_NAME} er opprettet') pip_bin = f'{VENV_NAME}/bin/pip3' if Path(REQUIREMENTS).exists(): print(f'Requirements-fil "{REQUIREMENTS}" funnet') print('Installerer requirements') subprocess.run([pip_bin, 'install', '-r', REQUIREMENTS]) print('Prosessen fullført! Aktiver miljøet med "source .venv/bin/activate"') else: print("Ingen requirements spesifisert ...")
I dette tilfellet bruker vi flere prosesser og analyserer dataene vi trenger i Python-skriptet. Vi bruker også pathlib biblioteket som lar oss sjekke om filen `requirements.txt` eksisterer.
Hvis du kjører Python-filen, vil du se nyttige meldinger om hva som skjer med operativsystemet.
❯ python setup.py Python funnet i: /usr/bin/python3 Ditt virtuelle miljø .venv er opprettet Requirements-fil "requirements.txt" funnet Installerer requirements Collecting asgiref==3.3.4 ....... Prosessen fullført! Aktiver miljøet med "source .venv/bin/activate"
Legg merke til at vi får output fra installasjonsprosessen fordi vi ikke omdirigerer standard output til en variabel.
Kjøring av Andre Programmeringsspråk
Vi kan kjøre andre programmeringsspråk med Python og hente output fra disse filene. Dette er mulig fordi underprosessene samhandler direkte med operativsystemet.
La oss for eksempel lage et «hello world»-program i C++ og Java. For å kjøre følgende fil, må du ha installert C++ og Java kompilatorer.
helloworld.cpp
#include <iostream> int main(){ std::cout << "Dette er en hello world i C++" << std::endl; return 0; }
helloworld.java
class HelloWorld{ public static void main(String args[]){ System.out.println("Dette er en hello world i Java"); } }
Jeg vet at dette er mye kode sammenlignet med en enkel Python-linje, men dette er bare for testformål.
Vi skal lage et Python-skript som kjører alle C++- og Java-filene i en katalog. For å gjøre dette vil vi først hente en liste over filer avhengig av filtypen, og glob lar oss gjøre det enkelt!
from glob import glob # Henter filer med hver filtype java_files = glob('*.java') cpp_files = glob('*.cpp')
Etter det kan vi begynne å bruke underprosesser til å utføre hver type fil.
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() + ' Forresten, dette ble kjørt av Python' 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() + ' En Python underprosess kjørte dette :)' print(output)
Et lite triks er å bruke strengfunksjonen `strip` for å endre output og bare få det vi trenger.
Merk: Vær forsiktig med å kjøre store Java- eller C++-filer, siden vi laster inn output i minnet, og dette kan føre til minnelekkasjer.
Åpne Eksterne Programmer
Vi kan kjøre andre programmer ved å kalle deres binære plassering gjennom en underprosess.
La oss prøve det ved å åpne Brave, min foretrukne nettleser.
import subprocess subprocess.run('brave')
Dette vil åpne en nettleserforekomst, eller bare en ny fane hvis du allerede har kjørt nettleseren.
Som med alle andre programmer som aksepterer flagg, kan vi bruke dem til å produsere ønsket oppførsel.
import subprocess subprocess.run(['brave', '--incognito'])
Oppsummering
En underprosess er en datamaskinprosess som er opprettet av en annen prosess. Vi kan sjekke prosessene som datamaskinen vår kjører med verktøy som htop og Oppgavebehandling.
Python har sitt eget bibliotek for å jobbe med underprosesser. For øyeblikket gir `run`-funksjonen oss et enkelt grensesnitt for å opprette og håndtere underprosesser.
Vi kan lage mange forskjellige applikasjoner med dem, fordi vi samhandler direkte med operativsystemet.
Husk til slutt at den beste måten å lære på er å lage noe du har lyst til å bruke.