Le module subprocess de la bibliothèque standard de Python permet d'exécuter de nouvelles applications et de gérer leurs entrées/sorties et leurs codes de retour. Il est conçu pour remplacer les fonctions et modules plus anciens comme os.system, os.spawn*, os.popen*, popen2.* et commands.*, offrant une interface plus puissante et flexible pour la création et la gestion de processus enfants.
Principales fonctions du module subprocess
Le module subprocess propose plusieurs fonctions pour différentes stratégies d'exécution de commandes :
subprocess.run(): La fonction recommandée pour la plupart des cas d'utilisation simples, car elle exécute la commande et attend sa fin. Elle renvoie un objetCompletedProcess.subprocess.Popen(): Fournit un contrôle plus fin sur le processus enfant. C'est le bloc de construction sous-jacent à partir duquel les autres fonctions sont implémentées. Il est utilisé lorsque vous avez besoin d'interagir avec le processus de manière asynchrone ou sur une longue période.subprocess.call(): Exécute une commande et attend qu'elle se termine, renvoyant son code de retour. Bien qu'il existe toujours,subprocess.run()est généralement préféré car il capture également la sortie et lève des exceptions en cas d'erreur.
Utilisation de subprocess.run()
La fonction subprocess.run() est la méthode la plus simple et la plus courante pour exécuter des commandes externes et attendre leur achèvement. Sa signature de base est la suivante :
subprocess.run(args, *, stdin=None, stdout=None, stderr=None, shell=False, cwd=None, timeout=None, check=False, encoding=None, text=None, capture_output=False)
Voici un aperçu des paramètres les plus couramment utilisés :
| Paramètre | Description |
|---|---|
args |
La commende à exécuter. Il peut s'agir d'une séquence de chaînes de caractères (ex: ['ls', '-l']) ou d'une seule chaîne (ex: 'ls -l'). Si args est une chaîne, shell doit être défini sur True. Il est généralement recommandé d'utiliser une séquence de chaînes pour des raisons de sécurité. |
stdin, stdout, stderr |
Contrôlent les flux d'entrée, de sortie et d'erreur du processus enfant. Par défaut, ils sont hérités du processus parent. Les valeurs courantes incluent subprocess.PIPE (pour capturer le flux), un descripteur de fichier ou un objet fichier. stderr=subprocess.STDOUT redirige l'erreur standard vers la sortie standard. |
shell |
Si True, la commande spécifiée sera exécutée via un shell système. Cela peut être pratique pour des commandes shell complexes, mais présente des risques de sécurité si args provient d'une source non fiable. |
cwd |
Définit le répertoire de travail courant pour le processus enfant. Si omis, le répertoire de travail du processus parent est utilisé. |
timeout |
Définit une durée maximale en secondes pour l'exécution de la commande. Si la commande dépasse ce délai, le processus enfant est terminé et une exception TimeoutExpired est levée. |
check |
Si True, une exception CalledProcessError est levée si le processus enfant se termine avec un code de retour non nul (indiquant une erreur). L'objet exception contient les arguments, le code de retour et les sorties capturées. |
encoding ou text |
Si spécifié, stdin, stdout et stderr recevront/renverront des chaînes de caractères au lieu d'octets, encodées selon la valeur de encoding. Si text=True (équivalent à encoding=True dans les versions plus anciennes), l'encodage par défaut du système est utilisé. |
capture_output |
Si True, stdout et stderr seront configurés sur subprocess.PIPE. C'est une manière pratique de capturer les sorties sans les spécifier individuellement. |
Exemple de base avec subprocess.run()
Voici comment exécuter une commande simple et analyser son résultat :
import subprocess
# Exécution d'une commande système pour lister le contenu du répertoire courant
# Nous utilisons 'capture_output=True' pour obtenir la sortie et l'erreur
# et 'text=True' pour que les sorties soient des chaînes de caractères.
commande = ["ls", "-la"]
resultat_execution = subprocess.run(
commande,
capture_output=True, # Capture stdout et stderr
text=True, # Décode les sorties en chaînes de caractères
timeout=10, # Temps maximum de 10 secondes
check=False # Ne lève pas d'exception pour les codes de retour non nuls
)
print(f"Objet CompletedProcess : {resultat_execution}")
print(f"Code de retour de la commande : {resultat_execution.returncode}")
if resultat_execution.stdout:
print(f"\n--- Sortie Standard (stdout) ---\n{resultat_execution.stdout}")
if resultat_execution.stderr:
print(f"\n--- Erreur Standard (stderr) ---\n{resultat_execution.stderr}")
# Exemple avec une commande qui n'existe pas pour illustrer l'erreur
commande_invalide = ["commande_qui_n_existe_pas"]
resultat_erreur = subprocess.run(
commande_invalide,
capture_output=True,
text=True
)
print(f"\n--- Résultat de la commande invalide (code {resultat_erreur.returncode}) ---")
print(f"Sortie standard : '{resultat_erreur.stdout.strip()}'")
print(f"Erreur standard :\n{resultat_erreur.stderr}")
Cas pratique : Récupération de l'état d'un processus
Ce cas montre comment exécuter une commande et récupérer son code de sortie ainsi que ses flux standard et d'erreur de manière structurée.
import subprocess
# Commande à exécuter pour obtenir des informations système (par exemple, le chemin actuel)
# Nous utilisons 'shell=True' ici pour montrer l'option, bien qu'une liste soit souvent préférée.
commande_shell_specifique = "pwd"
informations_processus = subprocess.run(
commande_shell_specifique,
shell=True,
capture_output=True,
text=True,
check=True # Lèvera une CalledProcessError si la commande échoue
)
print("--- Analyse de l'exécution ---")
print(f"Code de sortie du processus : {informations_processus.returncode}")
if informations_processus.stdout:
print(f"\nContenu de la sortie standard :\n{informations_processus.stdout.strip()}")
if informations_processus.stderr:
print(f"\nMessages d'erreur standard :\n{informations_processus.stderr.strip()}")
# Un autre exemple avec une commande UNIX courante
def obtenir_details_fichiers(chemin='.'):
"""Exécute 'ls -lh' pour un chemin donné et renvoie les détails."""
try:
details_commande = ["ls", "-lh", chemin]
process_info = subprocess.run(
details_commande,
capture_output=True,
text=True,
check=True # Vérifie le succès, lève une erreur sinon
)
return {
"code_retour": process_info.returncode,
"stdout": process_info.stdout,
"stderr": process_info.stderr
}
except subprocess.CalledProcessError as e:
return {
"code_retour": e.returncode,
"stdout": e.stdout,
"stderr": e.stderr,
"erreur": "La commande a échoué"
}
except FileNotFoundError:
return {"erreur": "La commande 'ls' n'a pas été trouvée."}
print("\n--- Détails des fichiers du répertoire courant ---")
resultats_ls = obtenir_details_fichiers()
print(f"Code de retour : {resultats_ls['code_retour']}")
print(f"Sortie :\n{resultats_ls['stdout']}")
if resultats_ls.get('stderr'):
print(f"Erreur :\n{resultats_ls['stderr']}")
if resultats_ls.get('erreur'):
print(f"Statut : {resultats_ls['erreur']}")