04: Il Modulo Subprocess - Pt. 1 - Lanciare Processi di Sistema

Python 3 - Lezioni su Concetti Intermedi

04: Il Modulo Subprocess - Pt. 1 - Lanciare Processi di Sistema

Ciao a tutti ragazzi e ragazze e benvenuti a una delle lezioni più interessanti tra quelle fatte fin'ora.

Vedremo come avviare nuovi processi di sistema con Python utilizzando il modulo subprocess incluso nella Standard Library. Si tratta di uno dei moduli più utilizzati e utili in assoluto, quindi se tutto va bene alla fine di questa lezione avrete tra le mani un nuovo e potente strumento di programmazione per i vostri script.

Un breve accenno sulla terminologia: quando parlo di avviare nuovi processi di sistema mi riferisco all'avviare programmi e funzionalità un pó come se ci trovassimo nella shell stessa di Linux o di Windows o del Mac.

>>> vlc
>>> ifconfig

Per semplificare la lezione l'ho divisa in due parti. In questa prima parte vedremo come usare la funzione run del modulo subprocess per lanciare nuovi sottoprocessi.

Una volta appresi questi concetti, entreremo nel dettaglio nella seconda parte vedendo come catturarne l'output per utilizzarlo poi in un secondo momento. Vi garantisco che tutto si dimostrerà molto più semplice di quanto ci si possa aspettare.

In questa puntata utilizzeremo Python non da IDLE come fatto fin'ora, ma bensí avviandolo dal terminale di sistema stesso. Vi basta scrivere Python o Python3 per avviarlo. Su Windows il comando python potrebbe darvi errore a seconda di come avete installato il pacchetto, in questo caso provate semplicemente py e dovrebbe funzionare.

Bene, iniziando importando i vari moduli. Io per comodità importo anche il modulo os, così da potermi spostare nel sistema.

import os
import subprocess

os.chdir("/home/pymike/Desktop")
os.listdir()

Come accennato, usiamo la funzione run() del modulo per avviare i vari processi, che accetta un comando, o una lista di comandi.

Ecco come fare:

subprocess.run(["vlc"])

Proviamo ora ad aprire un video in particolare:

subprocess.run(["vlc", "video_da_lanciare.mp4"])

Possiamo anche avviare script in altri linguaggi di programmazione, come Ruby:

subprocess.run(["ruby", "hello_ruby.rb"])

Fate attenzione a quest'ultima riga che ci viene mostrata in output: "CompletedProcess(args=['ruby', 'hello_ruby.rb'], returncode=0)".

Abbiamo la lista di parametri del comando lanciato, e poi il returncode. Questo codice, chiamato anche exit status, viene restituito dal sistema al termine di un processo: returncode=0 significa che tutto è filato liscio.

Proviamo ora un altro paio di comandi. Ad esempio, per avere informazioni sulle interfaccie di rete (su Windows è ipconfig) ci basta fare:

>>> subprocess.run(["ifconfig"])

(... output ...)
CompletedProcess(args=['ifconfig'], returncode=0)

E su Linux, possiamo anche richiamare comandi come super user: in questo caso, qualora stessimo eseguendo lo script come utente normale, ci verrà richiesta la password di root:

>>> subprocess.run(["sudo", "pacman", "-Syu"])

[sudo] password for pymike: 
:: Synchronizing package databases...

(... resto dell'output ...)
CompletedProcess(args=['sudo', 'pacman', '-Syu'], returncode=0)

La funzione run accetta più di un parametro; uno di questi è il parametro shell, che puó essere True o False, e di default è passato come False.

Se passiamo shell=True, il comando passato verrà eseguito richiamando prima di tutto una nuova shell di sistema e quindi eseguendolo attraverso di essa. Questo porta ad alcuni vantaggi ma anche ad alcuni svantaggi.

Tra i vantaggi, possiamo sfruttare al 100% la potenza della shell di comando del sistema operativo, utilizzando tutto il set di caratteri speciali messo a disposizione da ciascun sistema, ad esempio:

>>> subprocess.run("ls -l ~/Desktop",shell=True)

(... output ...)
CompletedProcess(args='ls -l ~/Desktop', returncode=0)

Se non avessi impostato shell=True, avrei ottenuto errore in quanto il carattere ~ non sarebbe stato riconosciuto dalla funzione run(). Inoltre come vedete, stavolta ho passato tutte le istruzioni sotto forma di stringa, il che facilita un poco magari l'invio dei comandi.

Il rovescio della medaglia, quindi lo svantaggio di questa tecnica è peró che comporta grosse falle di sicurezza: se non si sta attenti si possono causare danni irreversibili al sistema, specialmente se il comando da eseguire viene lanciato da utenti diciamo curiosi oppure semplicemente inesperti:

>>> from subprocess import run
>>> filename = input("What file would you like to display?\n")

What file would you like to display?
non_existent; rm -rf / #
>>> run("cat " + filename, shell=True) # Uh-oh. This will end badly..

[esempio tratto dalla documentazione ufficiale]

Quindi il mio consiglio è: se possibile, evitate di usare shell=True, a meno che non siate voi gli unici ad utilizzare questo programma!

Menu della Serie