Python subprocess / Popen avec un environnement modifié
je crois qu'exécuter une commande externe avec un environnement légèrement modifié est un cas très courant. C'est comme ça que j'ai tendance à le faire:
import subprocess, os
my_env = os.environ
my_env["PATH"] = "/usr/sbin:/sbin:" + my_env["PATH"]
subprocess.Popen(my_command, env=my_env)
j'ai le pressentiment qu'il y a une meilleure façon; est-il bien?
8 réponses
je pense que os.environ.copy()
est mieux si vous n'avez pas l'intention de modifier l'os.environ pour le processus en cours:
import subprocess, os
my_env = os.environ.copy()
my_env["PATH"] = "/usr/sbin:/sbin:" + my_env["PATH"]
subprocess.Popen(my_command, env=my_env)
cela dépend de la question. Si c'est pour cloner et modifier l'environnement une solution pourrait être:
subprocess.Popen(my_command, env=dict(os.environ, PATH="path"))
mais cela dépend un peu du fait que les variables remplacées sont des identificateurs Python valides, ce qu'ils sont le plus souvent (à quelle fréquence trouvez-vous des noms de variables d'environnement qui ne sont pas alphanumériques+underscore ou des variables qui commencent par un nombre?).
sinon vous pourriez écrire quelque chose comme:
subprocess.Popen(my_command, env=dict(os.environ,
**{"Not valid python name":"value"}))
dans le cas très étrange (à quelle fréquence utilisez-vous des codes de contrôle ou des caractères non ascii dans les noms de variables d'environnement?) que les clés de l'environnement sont bytes
vous ne pouvez même pas (sur python3) utiliser cette construction.
comme vous pouvez le voir les techniques (en particulier la première) utilisées ici avantages sur les clés de l'environnement est normalement valide identificateurs python, et également connu à l'avance (au moment du codage), la deuxième approche a des problèmes. Dans les cas où ce n'est pas le cas, vous devriez probablement chercher une autre approche .
vous pourriez utiliser my_env.get("PATH", '')
au lieu de my_env["PATH"]
dans le cas PATH
en quelque sorte Non défini dans l'environnement d'origine, mais autre que cela il semble très bien.
avec Python 3.5 vous pouvez le faire de cette façon:
import os
import subprocess
my_env = {**os.environ, 'PATH': '/usr/sbin:/sbin:' + os.environ['PATH']}
subprocess.Popen(my_command, env=my_env)
ici nous finissons avec une copie de os.environ
et dépassés PATH
valeur.
il a été rendu possible par PEP 448 (généralisations supplémentaires de déballage).
autre exemple. Si vous avez un environnement par défaut (i.e. os.environ
), et un dict avec lequel vous voulez modifier les valeurs par défaut, vous pouvez l'exprimer comme ceci:
my_env = {**os.environ, **dict_with_env_variables}
pour définir temporairement une variable d'environnement sans devoir copier l'os.objet envrion etc, je fais ceci:
process = subprocess.Popen(['env', 'RSYNC_PASSWORD=foobar', 'rsync', \
'rsync://username@foobar.com::'], stdout=subprocess.PIPE)
le paramètre env accepte un dictionnaire. Vous pouvez simplement prendre os.environ, ajouter une clé (de votre choix variable) (une copie de la dict si vous devez) et l'utiliser comme un paramètre à Popen
.
je sais que cela a été répondu depuis un certain temps, mais il y a des points que certains peuvent vouloir connaître l'utilisation de PYTHONPATH au lieu de PATH dans leur variable d'environnement. J'ai décrit une explication de l'exécution de scripts python avec cronjobs qui traite l'environnement modifié d'une manière différente ( trouvé ici ). Pensé qu'il pourrait être bon pour ceux qui, comme moi, juste un peu plus de cette réponse.
dans certaines circonstances, vous pouvez ne transmettre que les variables d'environnement dont votre sous-processus a besoin, mais je pense que vous avez la bonne idée en général (c'est comme ça que je le fais aussi).