Che cos'è un sottoprocesso in Python? [5 esempi di utilizzo]
Pubblicato: 2021-05-23I sottoprocessi ti consentono di interagire a un livello completamente nuovo con il sistema operativo.
Il nostro computer esegue continuamente sottoprocessi. In effetti, solo leggendo questo articolo, stai eseguendo molti processi come un gestore di rete o il browser Internet stesso.
La cosa bella di questo è che qualsiasi azione che facciamo sul nostro computer implica l'invocazione di un sottoprocesso. Ciò rimane vero anche se stiamo scrivendo un semplice script "ciao mondo" in Python.
Il concetto di sottoprocesso può sembrare oscuro anche se stai imparando a programmare da un po'. Questo articolo esaminerà in modo approfondito il concetto principale del sottoprocesso e come utilizzare la libreria standard del sottoprocesso Python.
Alla fine di questo tutorial, dovrai:
- Comprendere il concetto di sottoprocesso
- Ho imparato le basi della libreria dei sottoprocessi Python
- Hai messo alla prova le tue abilità di Python con esempi utili
Entriamo nel merito
Il concetto di sottoprocesso
In parole povere, un sottoprocesso è un processo informatico creato da un altro processo.
Possiamo pensare a un sottoprocesso come un albero, in cui ogni processo padre ha processi figlio in esecuzione dietro di esso. So che questo può essere piuttosto confuso, ma vediamolo con una semplice grafica.

Ci sono diversi modi in cui possiamo visualizzare il processo in esecuzione sul nostro computer. Ad esempio, in UNIX (Linux e MAC) abbiamo htop, che è un visualizzatore di processi interattivo.

La modalità ad albero è lo strumento più utile per dare un'occhiata ai sottoprocessi in esecuzione. Possiamo attivarlo con F5 .
Se osserviamo da vicino la sezione dei comandi, possiamo notare la struttura dei processi in esecuzione sul nostro computer.

Tutto inizia con /sbin/init che è il comando che avvia ogni processo sul nostro computer. Da quel punto, possiamo vedere l'inizio di altri processi come xfce4-screenshoter e xfce4-terminal (che conduce a ancora più sottoprocessi)
Dando un'occhiata a Windows, abbiamo il mitico task manager che risulta utile quando si eliminano quei programmi che si bloccano sulla nostra macchina.

Ora abbiamo un concetto chiarissimo. Vediamo come possiamo implementare i sottoprocessi in Python.
Sottoprocessi in Python
Un sottoprocesso in Python è un'attività che uno script Python delega al sistema operativo (OS).
La libreria dei sottoprocessi ci consente di eseguire e gestire i sottoprocessi direttamente da Python. Ciò implica lavorare con lo standard input stdin , lo standard output stdout e i codici di ritorno.
Non è necessario installarlo con PIP, poiché fa parte della libreria standard di Python.
Pertanto possiamo iniziare a utilizzare i sottoprocessi in Python semplicemente importando il modulo.
import subprocess # Using the module ....Nota: per seguire questo articolo dovresti avere Python 3.5 +
Per controllare la versione di Python che hai attualmente, esegui.
❯ python --version Python 3.9.5 # My resultNel caso in cui la versione di Python che ottieni sia 2.x puoi usare il seguente comando
python3 --versionContinuando con l'argomento, l'idea principale alla base della libreria dei sottoprocessi è quella di poter interagire con il sistema operativo eseguendo tutti i comandi che desideriamo, direttamente dall'interprete Python.
Ciò significa che possiamo fare tutto ciò che vogliamo, purché il nostro sistema operativo ce lo consenta (e purché non rimuovi il filesystem di root).
Vediamo come utilizzarlo creando un semplice script che elenchi i file della directory corrente.
Prima domanda di sottoprocesso
Per prima cosa, creiamo un file list_dir.py . Questo sarà il file in cui sperimenteremo l'elenco dei file.
touch list_dir.pyOra apriamo quel file e usiamo il seguente codice.
import subprocess subprocess.run('ls')Innanzitutto, stiamo importando il modulo subprocess e quindi utilizzando la funzione run che esegue, il comando che passiamo come argomento.
Questa funzione è stata introdotta in Python 3.5, come un'amichevole scorciatoia per subprocess.Popen. La funzione subprocess.run ci consente di eseguire un comando e attendere che finisca, a differenza di Popen dove abbiamo la possibilità di chiamare comunicare in seguito.
Parlando dell'output del codice, ls è un comando UNIX che elenca i file della directory in cui ti trovi. Pertanto, se esegui questo comando, otterrai un elenco dei file presenti nella directory corrente.
❯ python list_dir.py example.py LICENSE list_dir.py README.mdNota: tieni presente che se sei in Windows, dovrai utilizzare comandi diversi. Ad esempio invece di usare "ls" puoi usare "dir"
Questo può sembrare troppo semplice, e hai ragione. Vuoi avere un approccio completo a tutta la potenza che ti offre il guscio. Quindi impariamo come passare argomenti alla shell con subprocess.
Ad esempio per elencare anche i file nascosti (quelli che iniziano con un punto), ed elencare anche tutti i metadati dei file, scriviamo il seguente codice.
import subprocess # subprocess.run('ls') # Simple command subprocess.run('ls -la', shell=True)Stiamo eseguendo questo comando come una stringa e utilizzando l'argomento shell . Ciò significa che stiamo invocando una shell all'inizio dell'esecuzione del nostro sottoprocesso e l'argomento del comando viene interpretato direttamente dalla shell.
Tuttavia, l'uso di shell=True ha molti aspetti negativi e i peggiori sono le possibili perdite di sicurezza. Puoi leggere su di loro nella documentazione ufficiale.
Il modo migliore per passare i comandi alla funzione run è usare un elenco dove lst[0] è il comando da chiamare (ls in questo caso) e lst[n] sono gli argomenti di quel comando.
Se lo facciamo, il nostro codice sarà simile a questo.
import subprocess # subprocess.run('ls') # Simple command # subprocess.run('ls -la', shell=True) # Dangerous command subprocess.run(['ls', '-la'])Se vogliamo memorizzare l'output standard di un sottoprocesso in una variabile, possiamo farlo impostando l'argomento capture_output su true.
list_of_files = subprocess.run(['ls', '-la'], capture_output=True) print(list_of_files.stdout) ❯ python list_dir.py b'total 36\ndrwxr-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.py\ndrwxr-xr-x 8 daniel daniel 4096 may 20 17:31 .git\n-rw-r--r-- 1 daniel daniel 2160 may 17 22:23 .gitignore\n-rw-r--r-- 1 daniel daniel 271 may 20 19:53 internet_checker.py\n-rw-r--r-- 1 daniel daniel 1076 may 17 22:23 LICENSE\n-rw-r--r-- 1 daniel daniel 216 may 20 22:12 list_dir.py\n-rw-r--r-- 1 daniel daniel 22 may 17 22:23 README.md\n'Per accedere all'output di un processo, utilizziamo l'attributo di istanza stdout .
In questo caso, vogliamo memorizzare l'output come stringa, anziché byte e possiamo farlo impostando l'argomento del testo come vero.
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.mdPerfetto, ora che conosciamo le basi della libreria dei sottoprocessi , è ora di passare ad alcuni esempi di utilizzo.
Esempi di utilizzo di sottoprocessi in Python
In questa sezione, esamineremo alcuni usi pratici della libreria dei processi secondari. Puoi controllarli tutti in questo repository Github.
Controllo del programma
Uno degli usi principali di questa libreria è la capacità di eseguire semplici operazioni del sistema operativo.
Ad esempio, un semplice script che verifica se un programma è installato. In Linux, possiamo farlo con il comando 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)Nota: in UNIX, quando un comando ha successo, il suo codice di stato è 0. Altrimenti, qualcosa è andato storto durante l'esecuzione
Poiché non stiamo usando l'argomento shell=True , possiamo accettare l'input dell'utente in modo sicuro. Inoltre, possiamo verificare se l'input è un programma valido con un modello regex.
import subprocess import re programs = input('Separe the programs with a space: ').split() secure_pattern = '[\w\d]' 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')In questo caso, stiamo ottenendo i programmi dall'utente e utilizzando un'espressione regolare che certifica che la stringa del programma include solo lettere e cifre. Controlliamo l'esistenza di ogni programma con un ciclo for.
Grep semplice in Python
Il tuo amico Tom ha un elenco di modelli in un file di testo e un altro file di grandi dimensioni in cui vuole ottenere il numero di corrispondenze per ogni modello. Passava ore a eseguire il comando grep per ogni pattern.
Fortunatamente, sai come risolvere questo problema con Python e lo aiuterai a svolgere questo compito in pochi secondi.
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')Dando un'occhiata a questo file, definiamo due variabili che sono i nomi dei file con cui vogliamo lavorare. Quindi apriamo il file che contiene tutti i modelli e iteriamo su di essi. Successivamente, chiamiamo un sottoprocesso che esegue un comando grep con il flag "-c" (significa conteggio) e determina l'output della corrispondenza con un condizionale.
Se esegui questo file (ricorda che puoi scaricare i file di testo dal repository Github)
Configura un virtualenv con sottoprocesso
Una delle cose più interessanti che puoi fare con Python è l'automazione dei processi. Questo tipo di script può farti risparmiare ore di tempo alla settimana.
Ad esempio, creeremo uno script di installazione che crei un ambiente virtuale per noi e provi a trovare un file require.txt nella directory corrente per installare tutte le dipendenze.
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 ...")In questo caso, stiamo utilizzando più processi e analizzando i dati di cui abbiamo bisogno nel nostro script Python. Stiamo anche utilizzando la libreria pathlib che ci permette di capire se il file esiste requirements.txt.
Se esegui il file python, riceverai alcuni messaggi utili su ciò che sta accadendo con il sistema operativo.
❯ 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"Nota che otteniamo l'output dal processo di installazione perché non stiamo reindirizzando l'output standard a una variabile.
Esegui un altro linguaggio di programmazione
Possiamo eseguire altri linguaggi di programmazione con Python e ottenere l'output da quei file. Ciò è possibile perché i sottoprocessi interagiscono direttamente con il sistema operativo.
Ad esempio, creiamo un programma Hello World in C++ e Java. Per eseguire il seguente file, dovrai installare i compilatori C++ e Java.
ciaomondo.cpp
#include <iostream> int main(){ std::cout << "This is a hello world in C++" << std::endl; return 0; }
ciaomondo.java
class HelloWorld{ public static void main(String args[]){ System.out.println("This is a hello world in Java"); } }
So che questo sembra un sacco di codice rispetto a un semplice one-liner Python, ma questo è solo a scopo di test.
Creeremo uno script Python che esegua tutti i file C++ e Java in una directory. Per farlo prima vogliamo ottenere un elenco di file a seconda dell'estensione del file e glob ci consente di farlo facilmente!
from glob import glob # Gets files with each extension java_files = glob('*.java') cpp_files = glob('*.cpp')Successivamente, possiamo iniziare a utilizzare i sottoprocessi per eseguire ogni tipo di file.
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)Un piccolo trucco consiste nell'usare la striscia della funzione di stringa per modificare l'output e ottenere solo ciò di cui abbiamo bisogno.
Nota: prestare attenzione nell'eseguire file Java o C++ di grandi dimensioni poiché stiamo caricando il loro output in memoria e ciò potrebbe produrre una perdita di memoria.
Apri programmi esterni
Siamo in grado di eseguire altri programmi semplicemente chiamando la posizione dei loro binari attraverso un sottoprocesso.
Proviamolo aprendo brave , il mio browser web preferito.
import subprocess subprocess.run('brave')Verrà aperta un'istanza del browser o solo un'altra scheda se è già in esecuzione il browser.

Come con qualsiasi altro programma che accetta flag, possiamo usarli per produrre il comportamento desiderato.
import subprocess subprocess.run(['brave', '--incognito']) 
Per riassumere
Un sottoprocesso è un processo informatico creato da un altro processo. Possiamo controllare i processi in esecuzione sul nostro computer con strumenti come htop e il task manager.
Python ha la sua libreria per lavorare con i sottoprocessi. Attualmente, la funzione di esecuzione ci offre una semplice interfaccia per creare e gestire i sottoprocessi.
Possiamo creare qualsiasi tipo di applicazione con loro perché stiamo interagendo direttamente con il sistema operativo.
Infine, ricorda che il modo migliore per imparare è creare qualcosa che vorresti usare.
