web-dev-qa-db-fra.com

Python sous-processus / Ouvre un environnement modifié

Je pense 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 existe un meilleur moyen; ça a l'air bien?

247
Oren_H

Je pense que os.environ.copy() est préférable si vous n'avez pas l'intention de modifier 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)
348
Daniel Burke

Cela dépend de quel est le problème. S'il s'agit de cloner et de 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 identifiants valides python, qui sont-ils le plus souvent (combien de fois rencontrez-vous des noms de variable d'environnement qui ne sont pas alphanumériques + un trait de soulignement ou des variables commençant par un nombre?) .

Sinon, vous pourriez écrire quelque chose comme:

subprocess.Popen(my_command, env=dict(os.environ, 
                                      **{"Not valid python name":"value"}))

Dans des cas très étranges (à quelle fréquence utilisez-vous des codes de contrôle ou des caractères non ascii dans les noms de variables d’environnement?), Les clés de l’environnement sont bytes vous ne pouvez même pas (sur python3) utiliser cette construction.

Comme vous pouvez le constater, les techniques (en particulier la première) utilisées ici présentent des avantages sur les clés de l'environnement et sont normalement _ identifiants valides python, et également connues à l'avance (au moment du codage), la seconde approche pose des problèmes. Dans les cas où ce n'est pas le cas, vous devriez probablement rechercher ne autre approche .

57
skyking

vous pouvez utiliser my_env.get("PATH", '') au lieu de my_env["PATH"] au cas où PATH ne serait pas défini dans l'environnement d'origine, mais cela ne pose pas de problème.

23
SilentGhost

Avec Python 3.5, vous pouvez le faire comme suit:

import os
import subprocess

my_env = {**os.environ, 'PATH': '/usr/sbin:/sbin:' + os.environ['PATH']}

subprocess.Popen(my_command, env=my_env)

Nous nous retrouvons ici avec une copie de os.environ et une valeur _ PATH remplacée.

Cela a été rendu possible par PEP 448 (Générations de décompression supplémentaires).

Un autre exemple. Si vous avez un environnement par défaut (c'est-à-dire os.environ) et un dict avec lequel vous souhaitez remplacer les valeurs par défaut, vous pouvez l'exprimer comme suit:

my_env = {**os.environ, **dict_with_env_variables}
14
skovorodkin

Pour définir temporairement une variable d'environnement sans avoir à copier l'objet os.environ, etc., procédez comme suit:

process = subprocess.Popen(['env', 'RSYNC_PASSWORD=foobar', 'rsync', \
'rsync://[email protected]::'], stdout=subprocess.PIPE)
9
MFB

Le paramètre env accepte un dictionnaire. Vous pouvez simplement prendre os.environ, ajouter une clé (celle de votre choix) (à une copie du dict si vous le devez) et l’utiliser comme paramètre pour Popen.

3
Noufal Ibrahim

Je sais que cette question a été résolue depuis un certain temps, mais certains voudront peut-être savoir comment utiliser PYTHONPATH au lieu de PATH dans leur variable d'environnement. J'ai présenté une explication de l'exécution de python scripts avec cronjobs qui traite l'environnement modifié d'une manière différente ( trouvé ici ). Je pensais que ce serait une bonne chose pour ceux qui, comme moi, avaient besoin d'un peu plus que ce que cette réponse apportait.

1
derigible

Dans certaines circonstances, vous souhaiterez peut-être 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 ainsi que je le fais aussi).

0
Andrew Aylett