J'appelle différents processus avec le subprocess
module. Cependant, j'ai une question.
Dans les codes suivants:
callProcess = subprocess.Popen(['ls', '-l'], shell=True)
et
callProcess = subprocess.Popen(['ls', '-l']) # without shell
Les deux fonctionnent. Après avoir lu les documents, j'ai appris que cela shell=True
signifie exécuter le code via le shell. Cela signifie donc qu'en l'absence, le processus est directement démarré.
Alors, que dois-je préférer pour mon cas - j'ai besoin d'exécuter un processus et d'obtenir sa sortie. Quel avantage ai-je à l'appeler depuis l'intérieur ou l'extérieur de la coque.
python
subprocess
user225312
la source
la source
-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.shell=True
àFalse
et vice versa est une erreur. De la documentation : "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 existe une conversion automatique , ce qui peut être indésirable.Réponses:
L'avantage de ne pas appeler via le shell est que vous n'invoquez pas un «programme mystère». Sur POSIX, la variable d'environnement
SHELL
contrôle quel binaire est appelé en tant que "shell". Sous Windows, il n'y a pas de bourne shell descendant, seulement cmd.exe.Donc, invoquer le shell appelle un programme choisi par l'utilisateur et dépend de la plate-forme. De manière générale, évitez les invocations via le shell.
L'invocation via le shell vous permet d'étendre les variables d'environnement et les globes de fichiers selon le mécanisme habituel du shell. Sur les systèmes POSIX, le shell étend les globes de fichiers à une liste de fichiers. Sous Windows, un glob de fichier (par exemple, "*. *") N'est de toute façon pas développé par le shell (mais les variables d'environnement sur une ligne de commande sont développées par cmd.exe).
Si vous pensez que vous voulez des extensions de variables d'environnement et des globes de fichiers, recherchez les
ILS
attaques de 1992-ish sur les services réseau qui ont effectué des invocations de sous-programme via le shell. Les exemples incluent les diverssendmail
backdoors impliquantILS
.En résumé, utilisez
shell=False
.la source
$SHELL
n'est pas correcte. Pour citer subprocess.html: "Sous Unix avecshell=True
, le shell est par défaut/bin/sh
". (pas$SHELL
)open
si vous voulez des moyens sécurisés d'appeler un programme et de capturer la sortie.source: Module de sous-processus
la source
Un exemple où les choses pourraient mal se passer avec Shell = True est montré ici
Consultez la doc ici: subprocess.call ()
la source
L'exécution de programmes via le shell signifie que toutes les entrées utilisateur transmises au programme sont interprétées selon la syntaxe et les règles sémantiques du shell invoqué. Au mieux, cela ne cause que des inconvénients à l'utilisateur, car l'utilisateur doit respecter ces règles. Par exemple, les chemins contenant des caractères shell spéciaux comme les guillemets ou les blancs doivent être échappés. Au pire, cela provoque des fuites de sécurité, car l'utilisateur peut exécuter des programmes arbitraires.
shell=True
est parfois pratique d'utiliser des fonctionnalités de shell spécifiques comme le fractionnement de mots ou l'expansion de paramètres. Cependant, si une telle fonctionnalité est requise, utilisez d'autres modules qui vous sont fournis (par exempleos.path.expandvars()
pour l'expansion des paramètres oushlex
pour la division des mots). Cela signifie plus de travail, mais évite d'autres problèmes.En bref: évitez
shell=True
par tous les moyens.la source
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 simplementavec
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 dusubprocess
package fait ce que vous voulez. Si vous avez un Python assez récent, vous devriez probablement l'utilisersubprocess.run
.check=True
elle échouera si la commande que vous avez exécutée a échoué.stdout=subprocess.PIPE
elle capturera la sortie de la commande.universal_newlines=True
il décodera la sortie dans une chaîne Unicode appropriée (c'est justebytes
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, oucheck_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.la source
shell=True
, beaucoup avec d'excellentes réponses. Il vous est arrivé de choisir celui qui explique pourquoi à la place.subprocess