Co to jest podproces w Pythonie? [5 Usage Examples]

Podprocesy umożliwiają interakcję z systemem operacyjnym na zupełnie nowym poziomie.

Nasz komputer cały czas uruchamia podprocesy. W rzeczywistości, po prostu czytając ten artykuł, uruchamiasz wiele procesów, takich jak menedżer sieci lub sama przeglądarka internetowa.

Fajną rzeczą jest to, że każda akcja, którą wykonujemy na naszym komputerze, wymaga wywołania podprocesu. Pozostaje to prawdą, nawet jeśli piszemy prosty skrypt „hello world” w Pythonie.

Pojęcie podprocesu może wydawać się niejasne, nawet jeśli od jakiegoś czasu uczysz się programowania. W tym artykule dogłębnie przyjrzymy się głównej koncepcji podprocesu i sposobom korzystania z języka Python biblioteka standardowa podprocesu.

Pod koniec tego samouczka będziesz:

  • Zrozumieć pojęcie podprocesu
  • Poznałem podstawy biblioteki podprocesów Pythona
  • Ćwicz swoje umiejętności w Pythonie z przydatnymi przykładami

Wejdźmy w to

Pojęcie podprocesu

Mówiąc ogólnie, podproces to a proces komputerowy utworzone przez inny proces.

Możemy myśleć o podprocesie jako o drzewie, w którym za każdym procesem nadrzędnym działają procesy potomne. Wiem, że może to być dość mylące, ale zobaczmy to na prostej grafice.

Istnieje kilka sposobów wizualizacji procesu działającego na naszym komputerze. Na przykład w systemie UNIX (Linux i MAC) mamy htop, który jest interaktywną przeglądarką procesów.

Tryb drzewa jest najbardziej użytecznym narzędziem do przyjrzenia się uruchomionym podprocesom. Możemy go aktywować za pomocą F5.

Jeśli przyjrzymy się bliżej sekcji poleceń, możemy zauważyć strukturę procesów uruchomionych na naszym komputerze.

Wszystko zaczyna się od /sbin/init czyli polecenie, które uruchamia każdy proces na naszym komputerze. Od tego momentu możemy zobaczyć początek innych procesów, takich jak xfce4-screenshoter i xfce4-terminal (co sprzyja jeszcze większej liczbie podprocesów)

Patrząc na Windows, mamy mityczny menadżer zadań co jest przydatne podczas zabijania programów powodujących awarię na naszym komputerze.

Teraz mamy krystalicznie czystą koncepcję. Zobaczmy, jak możemy zaimplementować podprocesy w Pythonie.

Podprocesy w Pythonie

Podproces w Pythonie to zadanie, które skrypt Pythona deleguje do systemu operacyjnego (OS).

Biblioteka podprocesów pozwala nam wykonywać i zarządzać podprocesami bezpośrednio z Pythona. Obejmuje to pracę ze standardowym wejściem stdin, standardowym wyjściem stdout i kodami powrotu.

Nie musimy instalować go z PIP, ponieważ jest to część Pythona standardowa biblioteka.

Dlatego możemy zacząć używać podprocesów w Pythonie, po prostu importując moduł.

import subprocess

# Using the module ....

Uwaga: Aby postępować zgodnie z tym artykułem, powinieneś mieć Python 3.5 +

Aby sprawdzić aktualnie posiadaną wersję Pythona, po prostu uruchom.

❯ python --version
Python 3.9.5 # My result

Jeśli otrzymana wersja Pythona to 2.x, możesz użyć następującego polecenia

python3 --version

Kontynuując temat, główną ideą biblioteki podprocesów jest możliwość interakcji z systemem operacyjnym poprzez wykonywanie dowolnych poleceń bezpośrednio z interpretera Pythona.

Oznacza to, że możemy robić, co chcemy, o ile pozwala na to nasz system operacyjny (o ile nie usuniesz głównego systemu plików 😅).

Zobaczmy, jak go używać, tworząc prosty skrypt, który wyświetla listę plików w bieżącym katalogu.

Pierwsza aplikacja podprocesowa

Najpierw utwórzmy plik list_dir.py. To będzie plik, w którym będziemy eksperymentować z listą plików.

touch list_dir.py

Teraz otwórzmy ten plik i użyjmy następującego kodu.

import subprocess 

subprocess.run('ls')

Najpierw importujemy moduł subprocess, a następnie za pomocą funkcji run, która ją uruchamia, przekazujemy jako argument polecenie.

Ta funkcja została wprowadzona w Pythonie 3.5 jako przyjazny skrót do podproces.Popen. Funkcja subprocess.run pozwala nam uruchomić polecenie i poczekać na jego zakończenie, w przeciwieństwie do Popen, gdzie mamy możliwość wywołania komunikacji później.

Mówiąc o wyjściu kodu, ls to polecenie UNIX, które wyświetla listę plików katalogu, w którym się znajdujesz. Dlatego jeśli uruchomisz to polecenie, otrzymasz listę plików znajdujących się w bieżącym katalogu.

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

Uwaga: pamiętaj, że jeśli korzystasz z systemu Windows, będziesz musiał używać innych poleceń. Na przykład zamiast używać „ls” możesz użyć „dir”

To może wydawać się zbyt proste i masz rację. Chcesz w pełni wykorzystać całą moc, jaką zapewnia powłoka. Nauczmy się więc przekazywać argumenty do powłoki za pomocą subprocess.

Na przykład, aby wyświetlić również ukryte pliki (te, które zaczynają się od kropki), a także wyświetlić wszystkie metadane plików, piszemy następujący kod.

import subprocess

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

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

Uruchamiamy to polecenie jako ciąg znaków i używamy argumentu powłoki. Oznacza to, że na początku wykonywania naszego podprocesu wywołujemy powłokę, a argument polecenia jest interpretowany bezpośrednio przez powłokę.

Jednak użycie shell=True ma wiele wad, a najgorsze to możliwe wycieki bezpieczeństwa. Można o nich przeczytać w oficjalna dokumentacja.

Najlepszym sposobem przekazywania poleceń do funkcji run jest użycie listy, gdzie lst[0] to polecenie wywołania (w tym przypadku ls) i lst[n] są argumentami tego polecenia.

Jeśli to zrobimy, nasz kod będzie wyglądał tak.

import subprocess

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

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

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

Jeśli chcemy zapisać standardowe wyjście podprocesu w zmiennej, możemy to zrobić ustawiając argument capture_output na 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'

Aby uzyskać dostęp do wyjścia procesu, używamy atrybutu instancji stdout.

W tym przypadku chcemy zapisać dane wyjściowe jako łańcuch zamiast bajtów i możemy to zrobić, ustawiając argument tekstowy jako prawdziwy.

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

Świetnie, skoro już znamy podstawy biblioteki podprocesów, czas przejść do przykładów jej użycia.

Przykłady użycia podprocesu w Pythonie

W tej sekcji omówimy kilka praktycznych zastosowań biblioteki podprocesów. Możesz sprawdzić je wszystkie w tym Repozytorium Githuba.

Kontroler programu

Jednym z głównych zastosowań tej biblioteki jest możliwość wykonywania prostych operacji w systemie operacyjnym.

Na przykład prosty skrypt sprawdzający, czy program jest zainstalowany. W Linuksie możemy to zrobić za pomocą polecenia who.

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

Uwaga: W systemie UNIX, gdy polecenie zakończy się pomyślnie, jego kod statusu to 0. W przeciwnym razie coś poszło nie tak podczas wykonywania

Ponieważ nie używamy argumentu shell=True, możemy bezpiecznie przyjąć dane wprowadzane przez użytkownika. Możemy również sprawdzić, czy dane wejściowe są prawidłowym programem ze wzorcem wyrażenia regularnego.

import subprocess

import re

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

secure_pattern = 'pt.'

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

W tym przypadku pobieramy programy od użytkownika i używamy wyrażenia regularnego, które poświadcza, że ​​ciąg programu zawiera tylko litery i cyfry. Sprawdzamy istnienie każdego programu za pomocą pętli for.

Prosty Grep w Pythonie

Twój przyjaciel Tomek ma listę wzorców w pliku tekstowym i innym dużym pliku, w którym chce uzyskać liczbę dopasowań dla każdego wzorca. Spędzał godziny na uruchamianiu polecenia grep dla każdego wzorca.

Na szczęście wiesz, jak rozwiązać ten problem z Pythonem i pomożesz mu wykonać to zadanie w kilka 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')

Przyglądając się temu plikowi, definiujemy dwie zmienne, które są nazwami plików, z którymi chcemy pracować. Następnie otwieramy plik zawierający wszystkie wzorce i przeglądamy je. Następnie wywołujemy podproces, który uruchamia polecenie grep z flagą „-c” (oznacza liczbę) i określa wynik dopasowania za pomocą warunku.

Jeśli uruchomisz ten plik (pamiętaj, że możesz pobrać pliki tekstowe z Repozytorium Github)

Skonfiguruj virtualenv z subprocess

Jedną z najfajniejszych rzeczy, które możesz zrobić w Pythonie, jest automatyzacja procesów. Ten rodzaj skryptu może zaoszczędzić wiele godzin tygodniowo.

Na przykład stworzymy skrypt instalacyjny, który utworzy dla nas środowisko wirtualne i spróbuje znaleźć plik wymagania.txt w bieżącym katalogu, aby zainstalować wszystkie zależności.

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

W tym przypadku używamy wielu procesów i analizujemy potrzebne dane w naszym skrypcie Pythona. Używamy również tzw pathlib biblioteka, która pozwala nam zorientować się, czy plik wymagania.txt istnieje.

Jeśli uruchomisz plik Pythona, otrzymasz kilka przydatnych komunikatów o tym, co dzieje się z systemem operacyjnym.

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

Zauważ, że dane wyjściowe otrzymujemy z procesu instalacji, ponieważ nie przekierowujemy standardowego wyjścia do zmiennej.

Uruchom inny język programowania

Możemy uruchomić inne języki programowania za pomocą Pythona i uzyskać dane wyjściowe z tych plików. Jest to możliwe, ponieważ podprocesy wchodzą w bezpośrednią interakcję z systemem operacyjnym.

Na przykład stwórzmy program hello world w C++ i Javie. Aby wykonać poniższy plik, musisz go zainstalować C++ oraz Jawa kompilatory.

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

Wiem, że wydaje się to dużo kodu w porównaniu z prostym jednowierszowym Pythonem, ale to tylko do celów testowych.

Stworzymy skrypt Pythona, który uruchomi wszystkie pliki C++ i Java w katalogu. Aby to zrobić, najpierw chcemy uzyskać listę plików w zależności od rozszerzenia pliku i kula ziemska pozwala nam to łatwo zrobić!

from glob import glob

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

cpp_files = glob('*.cpp')

Następnie możemy zacząć używać podprocesów do wykonywania każdego typu pliku.

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)

Jedna mała sztuczka polega na użyciu paska funkcji łańcuchowej do zmodyfikowania danych wyjściowych i uzyskania tylko tego, czego potrzebujemy.

Uwaga: ostrożnie uruchamiaj duże pliki Java lub C++, ponieważ ładujemy ich dane wyjściowe do pamięci, co może spowodować wyciek pamięci.

Otwórz programy zewnętrzne

Jesteśmy w stanie uruchamiać inne programy, po prostu wywołując lokalizację ich plików binarnych za pośrednictwem podprocesu.

Wypróbujmy to, otwierając odważną, moją ulubioną przeglądarkę internetową.

import subprocess

subprocess.run('brave')

Spowoduje to otwarcie instancji przeglądarki lub innej karty, jeśli już uruchomiłeś przeglądarkę.

Podobnie jak w przypadku każdego innego programu, który akceptuje flagi, możemy ich użyć do uzyskania pożądanego zachowania.

import subprocess

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

Podsumowując

Podproces to proces komputerowy utworzony przez inny proces. Możemy sprawdzić procesy uruchomione na naszym komputerze za pomocą narzędzi takich jak htop i menedżer zadań.

Python ma własną bibliotekę do pracy z podprocesami. Obecnie funkcja run daje nam prosty interfejs do tworzenia i zarządzania podprocesami.

Możemy tworzyć z nimi wszelkiego rodzaju aplikacje, ponieważ wchodzimy w bezpośrednią interakcję z systemem operacyjnym.

Na koniec pamiętaj, że najlepszym sposobem na naukę jest stworzenie czegoś, czego chciałbyś używać.