На Python подпроцесс/к popen с модифицированной среде


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

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

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

8 205

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, которые имеют дело с измененной средой по-другому (найти здесь). Думал, что это будет полезно для тех, кто, как и я, нуждался в немного большем, чем этот ответ.

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