Aller au contenu principal

Exécuter des sous-processus en sécurité

Utilisez subprocess.run pour les commandes qui démarrent, se terminent et renvoient un résultat borné :

import subprocess

completed = subprocess.run(
["git", "status", "--short"],
check=True,
capture_output=True,
text=True,
encoding="utf-8",
timeout=30,
)
print(completed.stdout)

Passez l'exécutable et chaque argument comme éléments séparés d'une liste. Sans shell, espaces et métacaractères dans un argument restent partie de cet argument.

Contrats de résultat et d'échec

  • check=True lève CalledProcessError pour un état de sortie non nul.
  • capture_output=True capture les deux flux ; sans mode texte, ils sont en octets.
  • timeout= borne la communication après la création du processus. À expiration, run() termine et attend l'enfant avant de lever TimeoutExpired, mais certains délais de création propres aux plateformes ne sont pas interruptibles.
  • Capturer une sortie sans borne peut épuiser la mémoire. Héritez des flux, redirigez vers un fichier ou consommez progressivement si le volume est inconnu.

Traitez explicitement les états non nuls attendus au lieu de mettre check=False puis d'oublier returncode.

Environnement et répertoire de travail

import os
from pathlib import Path

child_env = os.environ.copy()
child_env["APP_MODE"] = "batch"

subprocess.run(
["tool", "--input", "data.json"],
cwd=Path("workspace"),
env=child_env,
check=True,
)

Une association env remplace l'environnement de l'enfant au lieu de le compléter. Ne partez d'une copie que si l'héritage est voulu. Un environnement minimal contrôlé réduit les dépendances cachées ; un environnement hérité peut transporter des identifiants et influencer la recherche d'exécutables ou bibliothèques.

Résolvez volontairement l'exécutable pour du code privilégié ou sensible. Ne supposez pas que le PATH de l'appelant est fiable.

Frontière du shell

N'utilisez shell=True que si les fonctions du langage shell sont réellement requises, pas comme raccourci pour construire une chaîne. Avec un shell, citations, expansions, pipelines, redirections et injections font partie du contrat. Ne concaténez jamais de texte non fiable dans une commande. Les règles diffèrent selon les plateformes, notamment pour les fichiers batch Windows.

Pour les enfants interactifs ou durables, utilisez Popen et assumez tout le cycle de vie : drainage des tubes, arrêt, signaux, délais et nettoyage. Employez asyncio.create_subprocess_exec si une orchestration asynchrone est réellement nécessaire. Appeler wait() pendant qu'un enfant peut remplir un tube non lu risque un interblocage ; communicate() coordonne les usages courants.

Une liste d'arguments ne valide pas l'entrée

Éviter le shell empêche ses expansions, pas l'interprétation par le programme enfant. Un nom de fichier commençant par - peut devenir une option ; utilisez -- seulement si l'outil documente cette convention. Les fichiers batch Windows peuvent passer par un shell même avec shell=False ; consultez les considérations de sécurité de subprocess.

Un échec au démarrage, par exemple un exécutable absent, lève OSError, souvent FileNotFoundError, plutôt que CalledProcessError. Le nettoyage après expiration de run() concerne l'enfant direct, sans garantir l'arrêt de tous ses descendants. Avec Popen.communicate(timeout=...), l'expiration ne tue pas automatiquement l'enfant : tuez-le, puis rappelez communicate() pour vider les tubes et récupérer son état.

Sources

Explorer les liens

Cette note n’a pas encore de liens vers d’autres notes.

Sur les mêmes sujets (49)

Ouvrir le réseau