Utforska Underprocesser: En Djupdykning i Python
Underprocesser ger dig en helt ny dimension av interaktion med ditt operativsystem.
Din dator kör ständigt underprocesser. Bara genom att läsa den här texten, aktiveras otaliga processer som exempelvis nätverkshanteraren och själva webbläsaren.
Det intressanta är att varje åtgärd du utför på datorn innebär anrop av en underprocess. Det gäller även om du skriver ett enkelt ”hej världen”-skript i Python.
Konceptet med underprocesser kan te sig diffust, även om du har programmerat ett tag. Den här artikeln utforskar djupare grunderna för underprocesser och hur du använder Pythons standardbibliotek för underprocesser.
Efter den här genomgången kommer du att:
- Förstå vad en underprocess är.
- Behärska grunderna i Pythons underprocessbibliotek.
- Ha övat dina Python-färdigheter med användbara exempel.
Låt oss börja!
Förstå Konceptet Underprocess
En underprocess är i grunden en datorprocess som skapas av en annan process.
Tänk dig underprocesser som ett träd, där varje överordnad process har underordnade processer som körs bakom kulisserna. Det kan vara lite förvirrande, så låt oss titta på en enkel grafisk representation.
Det finns flera sätt att visualisera processerna som körs på datorn. I UNIX-system (Linux & MAC) finns till exempel htop, en interaktiv processvisare.
Trädvyn är det mest användbara verktyget för att granska pågående underprocesser. Du aktiverar den med F5.
Om du tittar noga på kommandosektionen kan du se strukturen på processerna som körs på datorn.
Allt börjar med /sbin/init, kommandot som startar varje process på datorn. Därifrån ser du hur andra processer som xfce4-screenshoter och xfce4-terminal (vilket leder till fler underprocesser) startar.
I Windows har vi den välkända Aktivitetshanteraren, som är användbar när du behöver stänga av program som har kraschat.
Nu har du en tydligare bild av vad underprocesser är. Låt oss se hur vi kan använda underprocesser i Python.
Underprocesser i Python
I Python är en underprocess en uppgift som ett Python-skript delegerar till operativsystemet.
Med underprocessbiblioteket kan du köra och hantera underprocesser direkt från Python, vilket inkluderar att arbeta med standardinmatning (stdin), standardutmatning (stdout) och returkoder.
Du behöver inte installera det med PIP eftersom det är en del av Pythons standardbibliotek.
Det betyder att du kan börja använda underprocesser genom att helt enkelt importera modulen.
import subprocess # Använd modulen ....
Obs: För att följa den här artikeln behöver du Python 3.5+.
Du kan kontrollera vilken Python-version du har genom att köra:
❯ python --version Python 3.9.5 # Mitt resultat
Om du får version 2.x kan du använda följande kommando:
python3 --version
Huvudtanken bakom underprocessbiblioteket är att interagera med operativsystemet genom att köra kommandon direkt från Python-tolken.
Det betyder att du kan göra vad du vill, så länge operativsystemet tillåter det (och så länge du inte raderar ditt rotfilsystem 😅).
Låt oss skapa ett enkelt skript som listar filerna i den aktuella katalogen och se hur det fungerar.
Första Underprocesstillämpningen
Börja med att skapa en fil som heter list_dir.py. Det här blir filen där du experimenterar.
touch list_dir.py
Öppna filen och lägg in följande kod:
import subprocess subprocess.run('ls')
Först importerar du underprocessmodulen och sedan använder du funktionen run, som kör kommandot du skickar som argument.
Den här funktionen introducerades i Python 3.5 som ett smidigt alternativ till subprocess.Popen. Funktionen subprocess.run låter dig köra ett kommando och vänta på att det ska avslutas, till skillnad från Popen där du kan kommunicera med processen senare.
När det gäller kodens utmatning, så är ls ett UNIX-kommando som listar filerna i den aktuella katalogen. Om du kör kommandot får du en lista över filerna i katalogen.
❯ python list_dir.py example.py LICENSE list_dir.py README.md
Obs: Om du använder Windows måste du använda andra kommandon. Istället för att använda ”ls” kan du till exempel använda ”dir”.
Det kan verka enkelt, och det stämmer. Du vill utnyttja skalets fulla kraft. Låt oss lära oss hur du skickar argument till skalet med underprocess.
För att exempelvis lista dolda filer (de som börjar med en punkt) och all metadata, så skriver du följande kod:
import subprocess # subprocess.run('ls') # Enkelt kommando subprocess.run('ls -la', shell=True)
Du kör det här kommandot som en sträng och använder argumentet shell. Det betyder att ett skal anropas i början av exekveringen av underprocessen, och kommandoargumentet tolkas direkt av skalet.
Att använda shell=True har dock nackdelar, framförallt potentiella säkerhetsrisker. Du kan läsa mer om dem i den officiella dokumentationen.
Det bästa sättet att skicka kommandon till körfunktionen är att använda en lista där lst[0] är kommandot (ls i det här fallet) och lst[n] är argumenten till det kommandot.
Då ser koden ut så här:
import subprocess # subprocess.run('ls') # Enkelt kommando # subprocess.run('ls -la', shell=True) # Riskabelt kommando subprocess.run(['ls', '-la'])
Om du vill lagra standardutmatningen från en underprocess i en variabel, kan du göra det genom att sätta argumentet capture_output till 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'
Du kan komma åt utmatningen från en process via instansattributet stdout.
Om du vill lagra utmatningen som en sträng i stället för bytes kan du ange textargumentet till 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
Utmärkt, nu när du förstår grunderna i underprocessbiblioteket är det dags att gå vidare till några praktiska exempel.
Användningsexempel på Underprocesser i Python
I det här avsnittet går vi igenom några praktiska användningar av underprocessbiblioteket. Du kan se dem alla i detta Github-arkiv.
Programkontroll
En av de viktigaste användningsområdena för det här biblioteket är möjligheten att utföra grundläggande OS-operationer.
Till exempel ett enkelt skript som kontrollerar om ett program är installerat. I Linux kan du göra det med kommandot which.
'''Programkontroll med underprocess''' import subprocess program = 'git' process = subprocess. run(['which', program], capture_output=True, text=True) if process.returncode == 0: print(f'Programmet "{program}" är installerat') print(f'Den binära filen finns här: {process.stdout}') else: print(f'Tyvärr är {program} inte installerat') print(process.stderr)
Obs: I UNIX är statuskoden 0 om ett kommando lyckas. I annat fall har något gått fel under exekveringen.
Eftersom du inte använder argumentet shell=True, kan du ta användarinmatning på ett säkert sätt. Du kan även kontrollera om inmatningen är ett giltigt program med ett regexmönster.
import subprocess import re programs = input('Separera programmen med ett mellanslag: ').split() secure_pattern = 'sön' for program in programs: if not re.match(secure_pattern, program): print("Tyvärr kan vi inte kolla det programmet") continue process = subprocess. run( ['which', program], capture_output=True, text=True) if process.returncode == 0: print(f'Programmet "{program}" är installerat') print(f'Den binära filen finns här: {process.stdout}') else: print(f'Tyvärr är {program} inte installerat') print(process.stderr) print('n')
I det här fallet hämtar du programmen från användaren och använder ett regexuttryck som kontrollerar att programsträngen endast innehåller bokstäver och siffror. Sedan kontrollerar du om varje program finns, ett i taget.
Enkel Grep i Python
Din vän Tom har en lista med mönster i en textfil och en annan stor fil där han vill veta antalet matchningar för varje mönster. Han skulle få spendera timmar på att köra kommandot grep för varje mönster.
Som tur är vet du hur du löser det med Python och kan hjälpa honom att utföra den här uppgiften på några 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önstret "{pattern}" matchade inte någon rad i {readfile}') continue print(f'Mönstret "{pattern}" matchade {process.stdout.strip()} gånger')
I filen definierar du två variabler med filnamnen som ska användas. Sedan öppnar du filen med alla mönster och itererar över dem. Därefter anropar du en underprocess som kör ett grep-kommando med flaggan ”-c” (betyder antal) och kontrollerar resultatet av matchningen med ett villkor.
Om du kör den här filen (kom ihåg att du kan ladda ner textfilerna från Github-arkivet)
Konfigurera en virtualenv med underprocess
En av de coolaste sakerna du kan göra med Python är processautomatisering. Den här typen av skript kan spara många timmar i veckan.
Till exempel kan du skapa ett installationsskript som skapar en virtuell miljö och letar efter en requirements.txt-fil i den aktuella katalogen för att installera alla beroenden.
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('Tyvärr är python3 inte installerat') python_bin = process1.stdout.strip() print(f'Python hittades här: {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'Din venv {VENV_NAME} har skapats') pip_bin = f'{VENV_NAME}/bin/pip3' if Path(REQUIREMENTS).exists(): print(f'Requirements-filen "{REQUIREMENTS}" hittades') print('Installerar beroenden') subprocess.run([pip_bin, 'install', '-r', REQUIREMENTS]) print('Processen är klar! Aktivera miljön med "source .venv/bin/activate"') else: print("Inga requirements specificerade ...")
Här använder du flera processer och analyserar den data du behöver i Python-skriptet. Du använder även pathlib-biblioteket för att ta reda på om filen requirements.txt finns.
Om du kör Python-filen får du användbar information om vad som händer i operativsystemet.
❯ python setup.py Python hittades här: /usr/bin/python3 Din venv .venv har skapats Requirements-filen "requirements.txt" hittades Installerar beroenden Collecting asgiref==3.3.4 ....... Processen är klar! Aktivera miljön med "source .venv/bin/activate"
Observera att du får utmatning från installationsprocessen eftersom du inte omdirigerar standardutmatningen till en variabel.
Kör ett annat programmeringsspråk
Du kan köra andra programmeringsspråk med Python och få utmatningen från dessa filer. Det fungerar eftersom underprocesser interagerar direkt med operativsystemet.
Låt oss till exempel skapa ett hej världen-program i C++ och Java. För att köra följande fil måste du installera C++– och Java-kompilatorer.
helloworld.cpp
#include <iostream> int main(){ std::cout << "Det här är ett hej världen i C++" << std::endl; return 0; }
helloworld.java
class HelloWorld{ public static void main(String args[]){ System.out.println("Det här är ett hej världen i Java"); } }
Det här kan verka som mycket kod jämfört med en Python-etta, men det är bara för att testa.
Nu skapar du ett Python-skript som kör alla C++- och Java-filer i en katalog. Först vill du få en lista över filer baserat på filtillägget, och glob gör det enkelt!
from glob import glob # Hämtar filer med varje tillägg java_files = glob('*.java') cpp_files = glob('*.cpp')
Sedan kan du börja använda underprocesser för att köra varje filtyp.
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() + ' Förresten, det här kördes 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() + ' Det här kördes av en Python-underprocess :)' print(output)
Ett litet tips är att använda strängfunktionen strip för att modifiera utmatningen och bara få det du behöver.
Obs: Var försiktig när du kör stora Java- eller C++-filer eftersom du laddar utmatningen i minnet, vilket kan orsaka minnesläckor.
Öppna externa program
Du kan köra andra program genom att anropa deras binära plats med en underprocess.
Låt oss prova det genom att öppna Brave, min favoritwebbläsare.
import subprocess subprocess.run('brave')
Det här öppnar en webbläsarinstans, eller en ny flik om webbläsaren redan är öppen.
Som med alla andra program som accepterar flaggor kan du använda dem för att skapa önskat beteende.
import subprocess subprocess.run(['brave', '--incognito'])
Sammanfattningsvis
En underprocess är en datorprocess som skapas av en annan process. Du kan kontrollera processerna som datorn kör med verktyg som htop och aktivitetshanteraren.
Python har ett eget bibliotek för att hantera underprocesser. Körfunktionen ger dig ett enkelt gränssnitt för att skapa och hantera underprocesser.
Du kan skapa alla typer av applikationer med dem eftersom du interagerar direkt med operativsystemet.
Slutligen, kom ihåg att det bästa sättet att lära sig är att skapa något du vill använda.