Les autres réponses expliquent de manière adéquate les avertissements en matière de sécurité, qui sont également mentionnés dans le document intitulé subprocess
documentation. Mais en plus de cela, les frais généraux liés au lancement d'un shell pour lancer le programme que vous voulez exécuter sont souvent inutiles et définitivement stupides dans les situations où vous n'utilisez pas réellement les fonctionnalités du shell. De plus, la complexité cachée supplémentaire devrait vous effrayer, notamment si vous n'êtes pas très familier avec le shell ou les services qu'il fournit.
Lorsque les interactions avec l'interpréteur de commandes sont non triviales, vous exigez maintenant que le lecteur et le mainteneur du script de Python (qui peut ou non être votre futur moi) comprenne à la fois Python et le script de l'interpréteur de commandes. Rappelez-vous la devise de Python "L'explicite est meilleur que l'implicite" ; même lorsque le code Python sera un peu plus complexe que le script équivalent (et souvent très laconique) de l'interpréteur de commandes, il peut être préférable de supprimer l'interpréteur de commandes et de remplacer la fonctionnalité par des constructions Python natives. Minimiser le travail effectué dans un processus externe et garder le contrôle dans votre propre code autant que possible est souvent une bonne idée, simplement parce que cela améliore la visibilité et réduit les risques d'effets secondaires -- voulus ou non --.
L'expansion des caractères génériques, l'interpolation des variables et la redirection sont toutes simples à remplacer par des constructions Python natives. Un pipeline shell complexe dont certaines parties ou la totalité 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 néanmoins vous assurer que vous comprenez les implications en termes de performances et de sécurité.
Dans le cas trivial, pour éviter shell=True
il suffit de remplacer
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 le fait de citer des chaînes de caractères et de mettre en évidence les métacaractères de l'interpréteur de commandes n'est généralement pas nécessaire (ou utile, ou correct). Voir aussi Quand mettre des guillemets autour d'une variable shell ?
Si vous ne voulez pas vous débrouiller tout seul, les shlex.split()
La fonction peut le faire pour vous. Elle fait partie de la bibliothèque standard de Python, mais bien sûr, si votre chaîne de commande shell est statique, vous pouvez simplement l'exécuter une fois, pendant le développement, et coller le résultat dans votre script.
En passant, vous voulez très souvent éviter Popen
si l'un des wrappers les plus simples de la section subprocess
Le paquet fait ce que vous voulez. Si vous avez un Python assez récent, vous devriez probablement utiliser subprocess.run
.
- Con
check=True
il échouera si la commande que vous avez exécutée a échoué.
- Con
stdout=subprocess.PIPE
il capturera la sortie de la commande.
- Con
text=True
(ou de manière quelque peu obscure, avec le synonyme universal_newlines=True
) il décodera la sortie en une chaîne Unicode correcte (c'est juste bytes
dans l'encodage du système sinon, sur Python 3).
Sinon, pour de nombreuses tâches, vous voulez check_output
pour obtenir la sortie d'une commande, tout en vérifiant qu'elle a réussi, ou encore 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 portable". Même subprocess.run('echo "$HOME"', shell=True)
n'est pas portable sous Windows.
49 votes
La première commande est incorrecte :
-l
est transmis à/bin/sh
(le shell) au lieu dels
programme sous Unix sishell=True
. L'argument chaîne doit être utilisé avecshell=True
dans la plupart des cas, au lieu d'une liste.3 votes
Re "le processus est directement lancé" : Quoi ?
21 votes
L'affirmation "Les deux fonctionnent" à propos de ces deux appels est incorrecte et trompeuse. Les appels fonctionnent différemment. Il suffit de passer de
shell=True
aFalse
et vice versa est une erreur. De docs : "Sur POSIX avec shell=True, (...) Si args est une séquence, le premier élément spécifie la chaîne de commande, et tous les éléments supplémentaires seront traités comme des arguments supplémentaires pour le shell lui-même.". Sous Windows, il y a conversion automatique ce qui pourrait être indésirable.0 votes
Voir aussi stackoverflow.com/q/59641747/874188
1 votes
Note utile : vous pouvez alimenter une liste à call/Popen, mais il ignorera silencieusement tout sauf le premier élément de la liste. Python 3.5/Linux.