Vad är en delprocess i Python? [5 Usage Examples]

Underprocesser låter dig interagera på en helt ny nivå med operativsystemet.

Vår dator kör underprocesser hela tiden. Faktum är att bara genom att läsa den här artikeln kör du många processer som en nätverkshanterare eller själva webbläsaren.

Det häftiga med detta är att varje åtgärd vi gör på vår dator involverar att anropa en underprocess. Det förblir sant även om vi skriver ett enkelt ”hej världen”-skript i python.

Konceptet med delprocessen kan verka oklar även om du har lärt dig programmering ett tag. Den här artikeln kommer att ta en djup titt på huvudkonceptet för delprocessen och hur man använder Python underprocess standardbibliotek.

I slutet av denna handledning kommer du:

  • Förstå begreppet delprocess
  • Har lärt dig grunderna i Python-underprocessbiblioteket
  • Övade dina Python-färdigheter med användbara exempel

Låt oss gå in i det

Begreppet delprocess

I stora drag är en delprocess en datorprocess skapad av en annan process.

Vi kan tänka på en delprocess som ett träd, där varje förälderprocess har underordnade processer som körs bakom sig. Jag vet att detta kan vara ganska förvirrande, men låt oss se det med en enkel grafik.

Det finns flera sätt vi kan visualisera processen som körs på vår dator. Till exempel i UNIX (Linux & MAC) har vi htop, som är en interaktiv processvisare.

Trädläget är det mest användbara verktyget för att ta en titt på de pågående underprocesserna. Vi kan aktivera den med F5.

Om vi ​​tittar noga på kommandosektionen kan vi lägga märke till strukturen för de processer som körs på vår dator.

Allt börjar med /sbin/init vilket är kommandot som startar varje process på vår dator. Från den punkten kan vi se början av andra processer som xfce4-screenshoter och xfce4-terminalen (vilket leder till ännu mer underprocess)

När vi tittar på Windows har vi det mytiska Aktivitetshanteraren vilket är användbart när du dödar de kraschar program på vår maskin.

Nu har vi ett kristallklart koncept. Låt oss se hur vi kan implementera delprocesser i Python.

Underprocesser i Python

En underprocess i Python är en uppgift som ett python-skript delegerar till operativsystemet (OS).

Underprocessbiblioteket tillåter oss att exekvera och hantera underprocesser direkt från Python. Det innebär att arbeta med standardinmatningsstdin, standardutgångsstdout och returkoder.

Vi behöver inte installera det med PIP, eftersom det är en del av Python standardbibliotek.

Därför kan vi börja använda delprocesser i python bara genom att importera modulen.

import subprocess

# Using the module ....

Obs: För att följa den här artikeln bör du ha Python 3.5+

För att kontrollera vilken pythonversion du har för närvarande, bara kör.

❯ python --version
Python 3.9.5 # My result

Om Python-versionen du får är 2.x kan du använda följande kommando

python3 --version

För att fortsätta med ämnet, är huvudtanken bakom underprocessbiblioteket att kunna interagera med operativsystemet genom att utföra alla kommandon vi vill, direkt från Python-tolken.

Det betyder att vi kan göra vad vi vill, så länge som vårt operativsystem tillåter oss (Och så länge du inte tar bort ditt rotfilsystem 😅).

Låt oss se hur du använder det genom att skapa ett enkelt skript som listar filerna i den aktuella katalogen.

Första delprocessansökan

Låt oss först skapa en fil list_dir.py. Det här kommer att vara filen där vi ska experimentera med filer.

touch list_dir.py

Låt oss nu öppna den filen och använda följande kod.

import subprocess 

subprocess.run('ls')

Först importerar vi delprocessmodulen och använder sedan funktionen run som körs, kommandot vi skickar som ett argument.

Denna funktion introducerades i Python 3.5, som en vänlig genväg till subprocess.Popen. Funktionen subprocess.run låter oss köra ett kommando och vänta på att det ska avslutas, till skillnad från Popen där vi har möjlighet att ringa kommunicera senare.

På tal om kodutmatningen, ls är ett UNIX-kommando som listar filerna i katalogen du befinner dig i. Därför, om du kör det här kommandot, får du en lista över filerna som finns i den aktuella katalogen.

❯ python list_dir.py
example.py  LICENSE  list_dir.py  README.md

Obs: Tänk på att om du är i Windows måste du använda olika kommandon. Till exempel istället för att använda ”ls” kan du använda ”dir”

Detta kan tyckas för enkelt, och du har rätt. Du vill ta en full inställning till all kraft skalet ger dig. Så låt oss lära oss hur man skickar argument till skalet med subprocess.

Till exempel för att lista även de dolda filerna (de som börjar med en punkt), och även lista alla metadata för filerna, skriver vi följande kod.

import subprocess

# subprocess.run('ls')  # Simple command

subprocess.run('ls -la', shell=True)

Vi kör det här kommandot som en sträng och använder argumentskalet. Det betyder att vi anropar ett skal i början av exekveringen av vår delprocess, och kommandoargumentet tolkas direkt av skalet.

Användningsskalet=True har dock många nackdelar, och de värsta är de möjliga säkerhetsläckorna. Du kan läsa om dem i officiell dokumentation.

Det bästa sättet att skicka kommandon till körfunktionen är att använda en lista där lst[0] är kommandot att anropa (ls i det här fallet) och lst[n] är argumenten för det kommandot.

Om vi ​​gör det kommer vår kod att se ut så här.

import subprocess

# subprocess.run('ls')  # Simple command

# subprocess.run('ls -la', shell=True) # Dangerous command

subprocess.run(['ls', '-la'])

Om vi ​​vill lagra standardutdata från en delprocess i en variabel kan vi 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'

För att komma åt utdata från en process använder vi instansattributet stdout.

I det här fallet vill vi lagra utdata som en sträng istället för bytes och vi kan göra det genom att ställa in textargumentet som sant.

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, nu när vi känner till grunderna i underprocessbiblioteket är det dags att gå vidare till några användningsexempel.

Användningsexempel på delprocesser i Python

I det här avsnittet kommer vi att granska några praktiska användningar av underprocessbiblioteket. Du kan kontrollera dem alla i detta Github-förvaret.

Programkontroll

En av de viktigaste användningsområdena för detta bibliotek är möjligheten att göra enkla OS-operationer.

Till exempel ett enkelt skript som kontrollerar om ett program är installerat. I Linux kan vi göra detta med kommandot which.

'''Program checker with subprocess'''

import subprocess

program = 'git'

process = subprocess. run(['which', program], capture_output=True, text=True)

if process.returncode == 0: 
    print(f'The program "{program}" is installed')

    print(f'The location of the binary is: {process.stdout}')
else:
    print(f'Sorry the {program} is not installed')

    print(process.stderr)

Obs: I UNIX när ett kommando lyckas är dess statuskod 0. Annars gick något fel under exekveringen

Eftersom vi inte använder argumentet shell=True kan vi ta användarinmatningen säkert. Vi kan också kontrollera om ingången är ett giltigt program med ett regexmönster.

import subprocess

import re

programs = input('Separe the programs with a space: ').split()

secure_pattern = 'tis'

for program in programs:

    if not re.match(secure_pattern, program):
        print("Sorry we can't check that program")

        continue

    process = subprocess. run(
        ['which', program], capture_output=True, text=True)

    if process.returncode == 0:
        print(f'The program "{program}" is installed')

        print(f'The location of the binary is: {process.stdout}')
    else:
        print(f'Sorry the {program} is not installed')

        print(process.stderr)

    print('n')

I det här fallet hämtar vi programmen från användaren och använder ett regex-uttryck som intygar att programsträngen endast innehåller bokstäver och siffror. Vi kontrollerar existensen av varje program med en för en slinga.

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 få antalet matchningar för varje mönster. Han skulle spendera timmar på att köra kommandot grep för varje mönster.

Lyckligtvis vet du hur du löser detta problem med Python, och du kommer att hjälpa honom att utföra denna uppgift 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'The pattern "{pattern}" did not match any line of {readfile}')

            continue

        print(f'The pattern "{pattern}" matched {process.stdout.strip()} times')

När vi tittar på den här filen definierar vi två variabler som är de filnamn vi vill arbeta med. Sedan öppnar vi filen som innehåller alla mönster och itererar över dem. Därefter anropar vi en underprocess som kör ett grep-kommando med flaggan ”-c” (betyder antal) och bestämmer resultatet av matchningen med en villkorlig.

Om du kör den här filen (kom ihåg att du kan ladda ner textfilerna från Github repo)

Konfigurera en virtualenv med underprocess

En av de coolaste sakerna du kan göra med Python är processautomation. Den här typen av skript kan spara timmar per vecka.

Till exempel kommer vi att skapa ett installationsskript som skapar en virtuell miljö för oss och försöker hitta 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('Sorry python3 is not installed')

python_bin = process1.stdout.strip()

print(f'Python found 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'Your venv {VENV_NAME} has been created')

pip_bin = f'{VENV_NAME}/bin/pip3'

if Path(REQUIREMENTS).exists():
    print(f'Requirements file "{REQUIREMENTS}" found')
    print('Installing requirements')
    subprocess.run([pip_bin, 'install', '-r', REQUIREMENTS])

    print('Process completed! Now activate your environment with "source .venv/bin/activate"')

else:
    print("No requirements specified ...")

I det här fallet använder vi flera processer och analyserar de data vi behöver i vårt python-skript. Vi använder också pathlib bibliotek som låter oss ta reda på om filen requirements.txt finns.

Om du kör python-filen får du några användbara meddelanden om vad som händer med operativsystemet.

❯ python setup.py 
Python found in: /usr/bin/python3
Your venv .venv has been created
Requirements file "requirements.txt" found
Installing requirements
Collecting asgiref==3.3.4 .......
Process completed! Now activate your environment with "source .venv/bin/activate"

Observera att vi får utdata från installationsprocessen eftersom vi inte omdirigerar standardutdata till en variabel.

Kör ett annat programmeringsspråk

Vi kan köra andra programmeringsspråk med python och få utdata från dessa filer. Detta är möjligt eftersom delprocesserna interagerar direkt med det operativa systemet.

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 << "This is a hello world in C++" << std::endl;
    return 0;
}

helloworld.java

class HelloWorld{  
    public static void main(String args[]){  
     System.out.println("This is a hello world in Java");  
    }  
}  

Jag vet att det här verkar mycket kod jämfört med en enkel Python one-liner, men det här är bara för teständamål.

Vi kommer att skapa ett Python-skript som kör alla C++- och Java-filer i en katalog. För att göra detta först vill vi få en lista över filer beroende på filtillägget, och glob låter oss göra det enkelt!

from glob import glob

# Gets files with each extension
java_files = glob('*.java')

cpp_files = glob('*.cpp')

Efter det kan vi börja använda underprocesser för att exekvera varje typ av 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() + ' BTW this was runned by 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() + ' A Python subprocess runned this :)'
    print(output)

Ett litet knep är att använda strängfunktionsremsan för att modifiera utdata och bara få det vi behöver.

Obs: Var noga med att köra stora Java- eller C++-filer eftersom vi laddar deras utdata i minnet och det kan orsaka en minnesläcka.

Öppna externa program

Vi kan köra andra program bara genom att anropa deras binära plats genom en underprocess.

Låt oss prova det genom att öppna brave, min föredragna webbläsare.

import subprocess

subprocess.run('brave')

Detta öppnar en webbläsarinstans, eller bara en annan flik om du redan har kört webbläsaren.

Som med alla andra program som accepterar flaggor kan vi använda dem för att skapa önskat beteende.

import subprocess

subprocess.run(['brave', '--incognito'])

För att sammanfatta

En delprocess är en datorprocess skapad av en annan process. Vi kan kontrollera processerna som vår dator kör med verktyg som htop och aktivitetshanteraren.

Python har ett eget bibliotek för att arbeta med delprocesser. För närvarande ger körfunktionen oss ett enkelt gränssnitt för att skapa och hantera delprocesser.

Vi kan skapa vilken typ av applikation som helst med dem eftersom vi interagerar direkt med operativsystemet.

Slutligen, kom ihåg att det bästa sättet att lära sig är att skapa något du skulle vilja använda.