Co je podproces v Pythonu? [5 Usage Examples]

Podprocesy umožňují interakci s operačním systémem na zcela nové úrovni.

Náš počítač neustále spouští dílčí procesy. Ve skutečnosti pouhým přečtením tohoto článku spouštíte spoustu procesů, jako je správce sítě nebo samotný internetový prohlížeč.

Skvělé na tom je, že jakákoliv akce, kterou děláme na našem počítači, zahrnuje vyvolání podprocesu. To zůstává pravdou, i když píšeme jednoduchý skript „ahoj světe“ v pythonu.

Koncept podprocesu se může zdát nejasný, i když se už nějakou dobu učíte programování. Tento článek se podrobně podívá na hlavní koncept podprocesu a na to, jak používat Python standardní knihovna podprocesů.

Na konci tohoto tutoriálu budete:

  • Pochopit pojem podproces
  • Naučili jste se základy knihovny podprocesů Pythonu
  • Procvičte si své dovednosti v Pythonu pomocí užitečných příkladů

Pojďme do toho

Pojem podproces

Obecně řečeno, podproces je a počítačový proces vytvořený jiným procesem.

Podproces si můžeme představit jako strom, ve kterém za každým nadřízeným procesem běží podřízené procesy. Vím, že to může být docela matoucí, ale podívejme se na to pomocí jednoduché grafiky.

Existuje několik způsobů, jak můžeme vizualizovat proces běžící na našem počítači. Například v UNIXu (Linux & MAC) máme htop, což je interaktivní prohlížeč procesů.

Stromový režim je nejužitečnějším nástrojem pro pohled na běžící podprocesy. Můžeme jej aktivovat pomocí F5.

Pokud se podíváme pozorně do příkazové sekce, můžeme si všimnout struktury procesů běžících na našem počítači.

Všechno to začíná /sbin/init což je příkaz, který spouští každý proces v našem počítači. Od tohoto bodu můžeme vidět začátek dalších procesů, jako je xfce4-screenshoter a xfce4-terminál (což vede k ještě většímu podprocesu)

Když se podíváme na Windows, máme mýtické Správce úloh což je užitečné při zabíjení těchto havarujících programů na našem počítači.

Nyní máme křišťálově čistý koncept. Podívejme se, jak můžeme implementovat podprocesy v Pythonu.

Podprocesy v Pythonu

Podproces v Pythonu je úkol, který pythonský skript deleguje na operační systém (OS).

Knihovna podprocesů nám umožňuje spouštět a spravovat podprocesy přímo z Pythonu. To zahrnuje práci se standardním vstupním stdin, standardním výstupním stdout a návratovými kódy.

Nemusíme jej instalovat pomocí PIP, protože je součástí Pythonu standardní knihovna.

Proto můžeme začít používat podprocesy v pythonu pouhým importem modulu.

import subprocess

# Using the module ....

Poznámka: Chcete-li pokračovat spolu s tímto článkem, měli byste mít Python 3.5 +

Chcete-li zkontrolovat verzi pythonu, kterou aktuálně máte, stačí spustit.

❯ python --version
Python 3.9.5 # My result

V případě, že verze Pythonu, kterou získáte, je 2.x, můžete použít následující příkaz

python3 --version

Pokračujeme-li v tématu, hlavní myšlenkou knihovny podprocesů je schopnost interagovat s OS prováděním libovolných příkazů, které chceme, přímo z interpretru Pythonu.

To znamená, že si můžeme dělat, co chceme, pokud nám to náš OS dovolí (a pokud neodstraníte svůj kořenový souborový systém 😅).

Podívejme se, jak jej použít, vytvořením jednoduchého skriptu, který vypíše soubory aktuálního adresáře.

První podprocesová aplikace

Nejprve vytvořte soubor list_dir.py. Toto bude soubor, ve kterém budeme experimentovat se seznamem souborů.

touch list_dir.py

Nyní tento soubor otevřeme a použijeme následující kód.

import subprocess 

subprocess.run('ls')

Nejprve importujeme modul podprocesu a poté pomocí funkce run, která běží, příkaz předáme jako argument.

Tato funkce byla zavedena v Pythonu 3.5 jako přátelská zkratka podproces.Popen. Funkce subprocess.run nám umožňuje spustit příkaz a počkat na jeho dokončení, na rozdíl od Popen, kde máme možnost zavolat komunikaci později.

Když mluvíme o výstupu kódu, ls je příkaz UNIX, který uvádí soubory adresáře, ve kterém se nacházíte. Pokud tedy spustíte tento příkaz, získáte seznam souborů přítomných v aktuálním adresáři.

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

Poznámka: Počítejte s tím, že pokud jste ve Windows, budete muset používat různé příkazy. Například místo použití „ls“ můžete použít „dir“

Může se to zdát příliš jednoduché a máte pravdu. Chcete se plně přiblížit veškeré síle, kterou vám skořápka přináší. Pojďme se tedy naučit, jak předávat argumenty shellu pomocí subprocess.

Chcete-li například vypsat také skryté soubory (Ty, které začínají tečkou), a také vypsat všechna metadata souborů, napíšeme následující kód.

import subprocess

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

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

Tento příkaz spouštíme jako řetězec a používáme argument shell. To znamená, že na začátku provádění našeho podprocesu vyvoláváme shell a argument příkazu je interpretován přímo shellem.

Použití shell=True má však mnoho nevýhod a nejhorší jsou možné úniky zabezpečení. Můžete si o nich přečíst v oficiální dokumentace.

Nejlepší způsob, jak předat příkazy funkci run, je použít seznam, kde lst[0] je příkaz k volání (v tomto případě ls) a lst[n] jsou argumenty toho příkazu.

Pokud tak učiníme, náš kód bude vypadat takto.

import subprocess

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

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

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

Pokud chceme uložit standardní výstup podprocesu do proměnné, můžeme to udělat nastavením argumentu capture_output na hodnotu 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'

Pro přístup k výstupu procesu používáme atribut instance stdout.

V tomto případě chceme uložit výstup jako řetězec, namísto bajtů, a můžeme tak učinit nastavením textového argumentu jako 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

Perfektní, nyní, když známe základy knihovny podprocesů, je čas přejít k některým příkladům použití.

Příklady použití podprocesu v Pythonu

V této části se podíváme na některá praktická použití knihovny podprocesů. Všechny je můžete zkontrolovat v tomto úložiště Github.

Kontrola programu

Jedním z hlavních využití této knihovny je schopnost provádět jednoduché operace OS.

Například jednoduchý skript, který kontroluje, zda je program nainstalován. V Linuxu to můžeme udělat pomocí příkazu 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)

Poznámka: Když je příkaz v systému UNIX úspěšný, jeho stavový kód je 0. Jinak se během provádění něco pokazilo

Protože nepoužíváme argument shell=True, můžeme bezpečně převzít uživatelský vstup. Také můžeme zkontrolovat, zda je vstup platným programem s regulárním vzorem.

import subprocess

import re

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

secure_pattern = 'Út'

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')

V tomto případě získáváme programy od uživatele a používáme výraz regulárního výrazu, který potvrzuje, že řetězec programu obsahuje pouze písmena a číslice. Existenci každého programu zkontrolujeme pomocí cyklu for.

Jednoduchý Grep v Pythonu

Váš přítel Tom má seznam vzorů v textovém souboru a další velký soubor, ve kterém chce získat počet shod pro každý vzor. Strávil hodiny spouštěním příkazu grep pro každý vzor.

Naštěstí víte, jak tento problém vyřešit pomocí Pythonu, a pomůžete mu tento úkol splnit během několika sekund.

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')

Když se podíváme na tento soubor, definujeme dvě proměnné, což jsou názvy souborů, se kterými chceme pracovat. Poté otevřeme soubor, který obsahuje všechny vzory a iterujeme je. Dále zavoláme podproces, který spustí příkaz grep s příznakem „-c“ (znamená počet) a určí výstup shody s podmínkou.

Pokud spustíte tento soubor (nezapomeňte, že si můžete stáhnout textové soubory z Github repo)

Nastavte virtualenv s podprocesem

Jedna z nejlepších věcí, které můžete s Pythonem dělat, je automatizace procesů. Tento druh skriptu vám může ušetřit hodiny času týdně.

Například vytvoříme instalační skript, který nám vytvoří virtuální prostředí a pokusí se najít soubor requirements.txt v aktuálním adresáři pro instalaci všech závislostí.

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 ...")

V tomto případě používáme více procesů a analyzujeme data, která potřebujeme v našem python skriptu. Také používáme pathlib knihovna, která nám umožňuje zjistit, zda soubor requirements.txt existuje.

Pokud spustíte soubor python, dostanete několik užitečných zpráv o tom, co se děje s OS.

❯ 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"

Všimněte si, že výstup získáváme z instalačního procesu, protože nepřesměrováváme standardní výstup do proměnné.

Spusťte jiný programovací jazyk

Můžeme spouštět jiné programovací jazyky s pythonem a získat výstup z těchto souborů. To je možné, protože podprocesy interagují přímo s operačním systémem.

Vytvořme například program hello world v C++ a Javě. Chcete-li spustit následující soubor, musíte jej nainstalovat C++ a Jáva kompilátory.

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");  
    }  
}  

Vím, že se to zdá být hodně kódu ve srovnání s jednoduchým Python one-liner, ale je to jen pro testovací účely.

Vytvoříme skript Python, který spustí všechny soubory C++ a Java v adresáři. Nejprve chceme získat seznam souborů v závislosti na příponě souboru a glob nám to umožňuje snadno!

from glob import glob

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

cpp_files = glob('*.cpp')

Poté můžeme začít používat podprocesy ke spuštění každého typu souboru.

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)

Jeden malý trik je použít proužek funkcí řetězce k úpravě výstupu a získat pouze to, co potřebujeme.

Poznámka: Buďte opatrní, abyste spouštěli velké soubory Java nebo C++, protože jejich výstup načítáme do paměti a to by mohlo způsobit únik paměti.

Otevřete externí programy

Jsme schopni spouštět jiné programy pouhým voláním umístění jejich binárních souborů prostřednictvím podprocesu.

Pojďme to vyzkoušet otevřením brave, mého oblíbeného webového prohlížeče.

import subprocess

subprocess.run('brave')

Tím se otevře instance prohlížeče nebo jen další karta, pokud již máte spuštěný prohlížeč.

Stejně jako u jakéhokoli jiného programu, který přijímá příznaky, můžeme je použít k vytvoření požadovaného chování.

import subprocess

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

Abych to shrnul

Podproces je počítačový proces vytvořený jiným procesem. Můžeme zkontrolovat procesy, které náš počítač běží, pomocí nástrojů, jako je htop a správce úloh.

Python má svou vlastní knihovnu pro práci s podprocesy. V současné době nám funkce run poskytuje jednoduché rozhraní pro vytváření a správu podprocesů.

Můžeme s nimi vytvořit jakýkoli druh aplikace, protože komunikujeme přímo s OS.

Nakonec si pamatujte, že nejlepší způsob, jak se naučit, je vytvořit něco, co byste chtěli používat.