Подпроцесс Python / Popen с измененной средой

Я считаю, что выполнение внешней команды со слегка измененной средой является очень распространенным случаем. Вот как я это делаю:--2-->

import subprocess, os
my_env = os.environ
my_env["PATH"] = "/usr/sbin:/sbin:" + my_env["PATH"]
subprocess.Popen(my_command, env=my_env)

У меня есть чувство, что есть лучший способ; это выглядит нормально?

8 ответов


Я думаю os.environ.copy() лучше, если вы не собираетесь модифицировать ОС.окружающая среда для текущего процесса:

import subprocess, os
my_env = os.environ.copy()
my_env["PATH"] = "/usr/sbin:/sbin:" + my_env["PATH"]
subprocess.Popen(my_command, env=my_env)

Это зависит от того, какой вопрос. Если это клонирование и изменение среды, одним из решений может быть:

subprocess.Popen(my_command, env=dict(os.environ, PATH="path"))

но это несколько зависит от того, что замененные переменные являются допустимыми идентификаторами python, которыми они чаще всего являются (как часто вы сталкиваетесь с именами переменных среды, которые не являются буквенно-цифровыми+подчеркиванием или переменными, начинающимися с числа?).

В противном случае вы можете написать что-то вроде:

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

в очень странном случае (как часто вы используете управляющие коды или символы, отличные от ascii, в именах переменных среды?) что ключами среды являются bytes вы не можете (на python3) даже использовать эту конструкцию.

как вы можете видеть, методы (особенно первый), используемые здесь преимущества на ключах среды обычно являются допустимыми идентификаторами python, а также известны заранее (во время кодирования), второй подход имеет проблемы. В случаях, когда это не так, вы, вероятно, должны искать другой подход.


можно использовать my_env.get("PATH", '') вместо my_env["PATH"] в случае PATH как-то не определено в исходной среде, но в остальном это выглядит нормально.


С Python 3.5 вы можете сделать это следующим образом:

import os
import subprocess

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

subprocess.Popen(my_command, env=my_env)

здесь мы заканчиваем с копией os.environ и переопределить PATH значение.

это стало возможным благодаря PEP 448 (Дополнительные Распаковки Обобщений).

другой пример. Если у вас есть среда по умолчанию (т. е. os.environ), и дикт, который вы хотите переопределить по умолчанию, вы можете выразить это следующим образом:

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

чтобы временно установить переменную среды без необходимости копирования ОС.объект envrion и т. д., Я делаю это:

process = subprocess.Popen(['env', 'RSYNC_PASSWORD=foobar', 'rsync', \
'rsync://username@foobar.com::'], stdout=subprocess.PIPE)

параметр env принимает словарь. Вы можете просто взять ОС.environ, добавьте ключ (желаемую переменную) (к копии dict, если необходимо) и используйте его в качестве параметра для Popen.


Я знаю, что на это был дан ответ в течение некоторого времени, но есть некоторые моменты, которые некоторые могут захотеть узнать об использовании PYTHONPATH вместо PATH в их переменной среды. Я изложил объяснение запуска скриптов python с cronjobs, которые имеют дело с измененной средой по-другому (найти здесь). Подумал, что это будет полезно для тех, кому, как и мне, нужно немного больше, чем этот ответ.


в определенных обстоятельствах вы можете передавать только переменные среды, необходимые вашему подпроцессу, но я думаю, что у вас есть правильная идея в целом (вот как я это делаю).