Qu’est-ce qu’un sous-processus en Python ? [5 Usage Examples]

Les sous-processus vous permettent d’interagir à un niveau totalement nouveau avec le système opératoire.

Notre ordinateur exécute des sous-processus tout le temps. En fait, rien qu’en lisant cet article, vous exécutez de nombreux processus comme un gestionnaire de réseau ou le navigateur Internet lui-même.

Ce qui est cool, c’est que toute action que nous faisons sur notre ordinateur implique l’appel d’un sous-processus. Cela reste vrai même si nous écrivons un simple script « hello world » en python.

Le concept de sous-processus peut sembler obscur même si vous apprenez la programmation depuis un certain temps. Cet article examinera en profondeur le concept principal du sous-processus et comment utiliser Python bibliothèque standard de sous-processus.

À la fin de ce didacticiel, vous saurez :

  • Comprendre le concept de sous-processus
  • Avoir appris les bases de la bibliothèque de sous-processus Python
  • Pratiquez vos compétences Python avec des exemples utiles

Entrons dedans

Le concept de sous-processus

En gros, un sous-processus est un processus informatique créé par un autre processus.

Nous pouvons considérer un sous-processus comme un arbre, dans lequel chaque processus parent a des processus enfants qui s’exécutent derrière lui. Je sais que cela peut être assez déroutant, mais voyons-le avec un simple graphique.

Il existe plusieurs façons de visualiser le processus en cours d’exécution sur notre ordinateur. Par exemple, sous UNIX (Linux & MAC), nous avons htop, qui est un visualiseur de processus interactif.

Le mode arborescence est l’outil le plus utile pour jeter un œil aux sous-processus en cours d’exécution. Nous pouvons l’activer avec F5.

Si nous examinons de près la section de commande, nous pouvons remarquer la structure des processus en cours d’exécution sur notre ordinateur.

Tout commence par /sbin/init qui est la commande qui démarre chaque processus sur notre ordinateur. À partir de ce moment, nous pouvons voir le début d’autres processus comme xfce4-screenshoter et le xfce4-terminal (ce qui conduit à encore plus de sous-processus)

En jetant un œil à Windows, nous avons le mythique Gestionnaire des tâches ce qui est utile pour tuer ces programmes qui plantent sur notre machine.

Maintenant, nous avons un concept clair comme du cristal. Voyons comment nous pouvons implémenter des sous-processus en Python.

Sous-processus en Python

Un sous-processus en Python est une tâche qu’un script python délègue au système opérationnel (OS).

La bibliothèque de sous-processus nous permet d’exécuter et de gérer des sous-processus directement depuis Python. Cela implique de travailler avec l’entrée standard stdin, la sortie standard stdout et les codes de retour.

Nous n’avons pas à l’installer avec PIP, car il fait partie de Python bibliothèque standard.

Par conséquent, nous pouvons commencer à utiliser des sous-processus en python simplement en important le module.

import subprocess

# Using the module ....

Remarque : Pour suivre cet article, vous devez avoir Python 3.5 +

Pour vérifier la version de python que vous avez actuellement, exécutez simplement.

❯ python --version
Python 3.9.5 # My result

Si la version Python que vous obtenez est 2.x, vous pouvez utiliser la commande suivante

python3 --version

En continuant avec le sujet, l’idée principale derrière la bibliothèque de sous-processus est de pouvoir interagir avec le système d’exploitation en exécutant toutes les commandes que nous voulons, directement à partir de l’interpréteur Python.

Cela signifie que nous pouvons faire ce que nous voulons, tant que notre système d’exploitation nous le permet (et tant que vous ne supprimez pas votre système de fichiers racine 😅).

Voyons comment l’utiliser en créant un script simple qui liste les fichiers du répertoire courant.

Application du premier sous-processus

Commençons par créer un fichier list_dir.py. Ce sera le fichier dans lequel nous allons expérimenter la liste des fichiers.

touch list_dir.py

Ouvrons maintenant ce fichier et utilisons le code suivant.

import subprocess 

subprocess.run('ls')

Tout d’abord, nous importons le module de sous-processus, puis en utilisant la fonction run qui s’exécute, la commande que nous passons en argument.

Cette fonction a été introduite dans Python 3.5, en tant que raccourci convivial pour subprocess.Popen. La fonction subprocess.run nous permet d’exécuter une commande et d’attendre qu’elle se termine, contrairement à Popen où nous avons la possibilité d’appeler communiquer plus tard.

En ce qui concerne la sortie du code, ls est une commande UNIX qui répertorie les fichiers du répertoire dans lequel vous vous trouvez. Par conséquent, si vous exécutez cette commande, vous obtiendrez une liste des fichiers présents dans le répertoire actuel.

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

Remarque : Tenez compte du fait que si vous êtes sous Windows, vous devrez utiliser différentes commandes. Par exemple, au lieu d’utiliser « ls », vous pouvez utiliser « dir »

Cela peut sembler trop simple, et vous avez raison. Vous voulez adopter une approche complète de toute la puissance que la coque vous apporte. Apprenons donc à passer des arguments au shell avec subprocess.

Par exemple pour lister aussi les fichiers cachés (Ceux qui commencent par un point), et aussi lister toutes les métadonnées des fichiers, on écrit le code suivant.

import subprocess

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

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

Nous exécutons cette commande sous forme de chaîne et utilisons le shell d’argument. Cela signifie que nous invoquons un shell au début de l’exécution de notre sous-processus et que l’argument de la commande est interprété directement par le shell.

Cependant, l’utilisation shell=True a de nombreux inconvénients, et les pires sont les possibles fuites de sécurité. Vous pouvez lire à leur sujet dans le documents officiels.

La meilleure façon de passer des commandes à la fonction run est d’utiliser une liste où lst[0] est la commande à appeler (ls dans ce cas) et lst[n] sont les arguments de cette commande.

Si nous le faisons, notre code ressemblera à ceci.

import subprocess

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

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

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

Si nous voulons stocker la sortie standard d’un sous-processus dans une variable, nous pouvons le faire en définissant l’argument capture_output sur 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'

Pour accéder à la sortie d’un processus, nous utilisons l’attribut d’instance stdout.

Dans ce cas, nous voulons stocker la sortie sous forme de chaîne, au lieu d’octets et nous pouvons le faire en définissant l’argument text sur 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

Parfait, maintenant que nous connaissons les bases de la bibliothèque de sous-processus, il est temps de passer à quelques exemples d’utilisation.

Exemples d’utilisation de sous-processus en Python

Dans cette section, nous allons passer en revue quelques utilisations pratiques de la bibliothèque de sous-processus. Vous pouvez tous les vérifier dans ce Référentiel Github.

Vérificateur de programme

L’une des principales utilisations de cette bibliothèque est la possibilité d’effectuer des opérations simples sur le système d’exploitation.

Par exemple, un simple script qui vérifie si un programme est installé. Sous Linux, nous pouvons le faire avec la commande 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)

Remarque : Sous UNIX, lorsqu’une commande réussit, son code d’état est 0. Sinon, quelque chose s’est mal passé lors de l’exécution

Puisque nous n’utilisons pas l’argument shell=True, nous pouvons prendre l’entrée de l’utilisateur en toute sécurité. De plus, nous pouvons vérifier si l’entrée est un programme valide avec un modèle regex.

import subprocess

import re

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

secure_pattern = 'sam'

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

Dans ce cas, nous obtenons les programmes de l’utilisateur et utilisons une expression regex qui certifie que la chaîne de programme ne comprend que des lettres et des chiffres. Nous vérifions l’existence de chaque programme avec une boucle for a.

Grep simple en Python

Votre ami Tom a une liste de modèles dans un fichier texte et un autre gros fichier dans lequel il veut obtenir le nombre de correspondances pour chaque modèle. Il passait des heures à exécuter la commande grep pour chaque modèle.

Heureusement, vous savez comment résoudre ce problème avec Python, et vous l’aiderez à accomplir cette tâche en quelques secondes.

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

En regardant ce fichier, nous définissons deux variables qui sont les noms de fichiers avec lesquels nous voulons travailler. Ensuite, nous ouvrons le fichier qui contient tous les modèles et les parcourons. Ensuite, nous appelons un sous-processus qui exécute une commande grep avec le drapeau « -c » (signifie compter) et déterminons la sortie de la correspondance avec une condition.

Si vous exécutez ce fichier (n’oubliez pas que vous pouvez télécharger les fichiers texte à partir du Dépôt Github)

Configurer un virtualenv avec un sous-processus

L’une des choses les plus intéressantes que vous puissiez faire avec Python est l’automatisation des processus. Ce type de script peut vous faire gagner des heures par semaine.

Par exemple, nous allons créer un script d’installation qui crée un environnement virtuel pour nous et essaie de trouver un fichier requirements.txt dans le répertoire courant pour installer toutes les dépendances.

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

Dans ce cas, nous utilisons plusieurs processus et analysons les données dont nous avons besoin dans notre script python. Nous utilisons également le pathlib librairie qui nous permet de savoir si le fichier requirements.txt existe.

Si vous exécutez le fichier python, vous obtiendrez des messages utiles sur ce qui se passe avec le système d’exploitation.

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

Notez que nous obtenons la sortie du processus d’installation car nous ne redirigeons pas la sortie standard vers une variable.

Exécuter un autre langage de programmation

Nous pouvons exécuter d’autres langages de programmation avec python et obtenir la sortie de ces fichiers. Ceci est possible parce que les sous-processus interagissent directement avec le système d’exploitation.

Par exemple, créons un programme hello world en C++ et Java. Pour exécuter le fichier suivant, vous devez installer C++ et Java compilateurs.

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

Je sais que cela semble beaucoup de code par rapport à un simple Python one-liner, mais c’est juste à des fins de test.

Nous allons créer un script Python qui exécute tous les fichiers C++ et Java dans un répertoire. Pour ce faire, nous voulons d’abord obtenir une liste de fichiers en fonction de l’extension de fichier, et global nous permet de le faire facilement !

from glob import glob

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

cpp_files = glob('*.cpp')

Après cela, nous pouvons commencer à utiliser des sous-processus pour exécuter chaque type de fichier.

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)

Une petite astuce consiste à utiliser la bande de fonctions de chaîne pour modifier la sortie et obtenir uniquement ce dont nous avons besoin.

Remarque : soyez prudent lorsque vous exécutez des fichiers Java ou C++ volumineux, car nous chargeons leur sortie en mémoire, ce qui pourrait entraîner une fuite de mémoire.

Ouvrir des programmes externes

Nous sommes capables d’exécuter d’autres programmes simplement en appelant leur emplacement binaire via un sous-processus.

Essayons-le en ouvrant courageux, mon navigateur Web préféré.

import subprocess

subprocess.run('brave')

Cela ouvrira une instance de navigateur, ou juste un autre onglet si vous avez déjà exécuté le navigateur.

Comme avec tout autre programme qui accepte les drapeaux, nous pouvons les utiliser pour produire le comportement souhaité.

import subprocess

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

Pour résumer

Un sous-processus est un processus informatique créé par un autre processus. Nous pouvons vérifier les processus en cours d’exécution sur notre ordinateur avec des outils tels que htop et le gestionnaire de tâches.

Python a sa propre bibliothèque pour travailler avec des sous-processus. Actuellement, la fonction run nous offre une interface simple pour créer et gérer des sous-processus.

Nous pouvons créer n’importe quel type d’application avec eux car nous interagissons directement avec le système d’exploitation.

Enfin, rappelez-vous que la meilleure façon d’apprendre est de créer quelque chose que vous aimeriez utiliser.