Les autres réponses ici expliquent de manière adéquate les avertissements de sécurité qui sont également mentionnés dans la subprocess
documentation. Mais en plus de cela, la surcharge de démarrage d'un shell pour démarrer le programme que vous souhaitez exécuter est souvent inutile et définitivement idiote dans les situations où vous n'utilisez aucune des fonctionnalités du shell. De plus, la complexité cachée supplémentaire devrait vous faire peur, surtout si vous n'êtes pas très familier avec le shell ou les services qu'il fournit.
Lorsque les interactions avec le shell ne sont pas triviales, vous avez maintenant besoin que le lecteur et le responsable du script Python (qui peut ou non être votre futur moi) comprennent à la fois Python et le script shell. Souvenez-vous de la devise Python "explicite vaut mieux qu'implicite"; même lorsque le code Python va être un peu plus complexe que le script shell équivalent (et souvent très laconique), il vaut mieux supprimer le shell et remplacer la fonctionnalité par des constructions Python natives. Minimiser le travail effectué dans un processus externe et garder le contrôle de votre propre code dans la mesure du possible est souvent une bonne idée simplement parce qu'il améliore la visibilité et réduit les risques d'effets secondaires voulus ou indésirables.
L'expansion des caractères génériques, l'interpolation variable et la redirection sont toutes simples à remplacer par des constructions Python natives. Un pipeline de shell complexe où certaines parties ou toutes ne peuvent pas être raisonnablement réécrites en Python serait la seule situation où vous pourriez peut-être envisager d'utiliser le shell. Vous devez toujours vous assurer que vous comprenez les implications en termes de performances et de sécurité.
Dans le cas trivial, pour éviter shell=True
, remplacez simplement
subprocess.Popen("command -with -options 'like this' and\\ an\\ argument", shell=True)
avec
subprocess.Popen(['command', '-with','-options', 'like this', 'and an argument'])
Remarquez comment le premier argument est une liste de chaînes à passer execvp()
et comment les guillemets et les métacaractères shell échappant les barres obliques inverses ne sont généralement pas nécessaires (ou utiles ou corrects). Peut-être voir aussi Quand mettre des guillemets autour d'une variable shell?
En passant, vous voulez très souvent éviter Popen
si l'un des wrappers les plus simples du subprocess
package fait ce que vous voulez. Si vous avez un Python assez récent, vous devriez probablement l'utiliser subprocess.run
.
- Avec
check=True
elle échouera si la commande que vous avez exécutée a échoué.
- Avec
stdout=subprocess.PIPE
elle capturera la sortie de la commande.
- Un peu obscurément,
universal_newlines=True
il décodera la sortie dans une chaîne Unicode appropriée (c'est juste bytes
dans le codage du système sinon, sur Python 3).
Sinon, pour de nombreuses tâches, vous souhaitez check_output
obtenir la sortie d'une commande, tout en vérifiant qu'elle a réussi, ou check_call
s'il n'y a pas de sortie à collecter.
Je terminerai par une citation de David Korn: "Il est plus facile d'écrire un shell portable qu'un script shell portable." Même subprocess.run('echo "$HOME"', shell=True)
n'est pas portable pour Windows.
-l
est passée à/bin/sh
(le shell) au lieu duls
programme sous Unix sishell=True
. L'argument chaîne doit être utilisé avecshell=True
dans la plupart des cas au lieu d'une liste.