Signification réelle de "shell=True' dans le sous-processus
J'appelle différents processus avec le module subprocess
. 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 shell=True
signifie exécuter le code à travers le shell. Donc, cela signifie qu'en l'absence, le processus est directement démarré.
Alors, Que devrais - je préférer pour mon cas-j'ai besoin d'exécuter un processus et d'obtenir sa sortie. Quel avantage ai-je de l'appeler de l'intérieur ou de l'extérieur de il.
5 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é comme "shell"."Sous Windows, il n'y a pas de descendant de bourne shell, seulement cmd.EXE.
Donc, l'appel du shell appelle un programme du choix de l'utilisateur et dépend de la plate-forme. D'une manière générale, évitez les invocations via le shell.
L'appel via le shell vous permet d'étendre les variables d'environnement et le fichier globs selon le mécanisme habituel de la coquille. Sur les systèmes POSIX, le shell étend les globes de fichiers à une liste de fichiers. Sous Windows, un fichier glob (par exemple,"*.*") n'est pas développé par le shell, de toute façon (mais les variables d'environnement sur une ligne de commande sont développées par cmd.EXE).
Si vous pensez que vous voulez des expansions de variables d'environnement et des globes de fichiers, recherchez les attaques ILS
de 1992-ish sur les services réseau qui ont effectué des invocations de sous-programmes via le shell. Les exemples incluent les différents sendmail
backdoors impliquant ILS
.
En résumé, utilisez shell=False
.
>>> import subprocess
>>> subprocess.call('echo $HOME')
Traceback (most recent call last):
...
OSError: [Errno 2] No such file or directory
>>>
>>> subprocess.call('echo $HOME', shell=True)
/user/khong
0
La définition de l'argument shell sur une valeur true entraîne le sous-processus à générer un processus shell intermédiaire, et lui dire d'exécuter la commande. En d'autres termes, l'utilisation d'un shell intermédiaire signifie que les variables, les motifs glob et d'autres fonctionnalités shell spéciales dans la chaîne de commande sont traitées avant l'exécution de la commande. Ici, dans l'exemple, $HOME a été traité avant la commande echo. En fait, c'est le cas de la commande avec l'expansion du shell alors que la commande ls-l considérée comme une commande simple.
Source: Module De Sous-Processus
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 entraîne des inconvénients pour l'utilisateur, car l'utilisateur doit obéir à ces règles. Par exemple, les chemins contenant des caractères shell spéciaux tels que des guillemets ou des 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 pour utiliser des fonctionnalités de shell spécifiques comme le fractionnement de mots ou l'expansion des paramètres. Cependant, si une telle fonctionnalité est requise, utilisez d'autres modules qui vous sont donnés (par exemple os.path.expandvars()
pour l'expansion des paramètres ou shlex
pour le fractionnement des mots). Cela signifie plus de travail, mais évite d'autres problèmes.
En bref: Évitez shell=True
par tous les moyens.
Un exemple où les choses pourraient mal tourner avec Shell = True est montré ici
>>> from subprocess import call
>>> filename = input("What file would you like to display?\n")
What file would you like to display?
non_existent; rm -rf / # THIS WILL DELETE EVERYTHING IN ROOT PARTITION!!!
>>> call("cat " + filename, shell=True) # Uh-oh. This will end badly...
Vérifiez le document ici: sous-processus.appel()
Les autres réponses ici expliquent adéquatement les mises en garde de sécurité qui sont également mentionnées dans la documentation subprocess
. 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 stupide pour les situations où vous n'utilisez aucune des fonctionnalités du shell. De plus, la complexité cachée supplémentaire devrait vous effrayer, surtout Si vous n'êtes pas très familier avec le shell ou les services qu'il fournit.
L'extension générique, l'interpolation de variables et la redirection sont toutes simples à remplacer par des constructions Python natives. Un pipeline shell complexe où des parties ou tous ne peuvent pas être raisonnablement réécrits en Python (outils externes spécialisés, peut-être source fermée?) serait la seule situation où vous pourriez peut-être envisager d'utiliser le shell. Vous devriez toujours se sentir mal.
Dans le cas trivial, 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 de chaînes et les métacaractères shell échappant à une barre oblique inverse ne sont généralement pas nécessaires (ou utiles, ou corrects).
En aparté, vous voulez très souvent éviter Popen
si l'un des wrappers les plus simples du paquet subprocess
fait ce que vous voulez. Si vous avez un python assez récent, vous devriez probablement utiliser subprocess.run
.
- Avec
check=True
, il échouera si la commande exécutée échoué. - Avec
stdout=subprocess.PIPE
, il sera la capture de la sortie de la commande. - un peu obscurément, avec
universal_newlines=True
Il va décoder la sortie en une chaîne Unicode appropriée (c'est justebytes
dans le codage système sinon, sur Python 3).
Si pas, pour de nombreuses tâches, vous voulez check_output
pour obtenir la sortie d'une commande, tout en vérifiant qu'il a réussi, ou check_call
si 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 portable script shell."Même subprocess.run('echo "$HOME"', shell=True)
n'est pas portable Pour Windows.