Подпроцесс Python/Popen с измененной средой
-
19-09-2019 - |
Вопрос
Я считаю, что запуск внешней команды со слегка измененной средой — очень распространенный случай.Вот как я обычно это делаю:
import subprocess, os
my_env = os.environ
my_env["PATH"] = "/usr/sbin:/sbin:" + my_env["PATH"]
subprocess.Popen(my_command, env=my_env)
У меня есть нутром ощущение, что есть лучший способ;выглядит нормально?
Решение
Я думаю os.environ.copy()
лучше, если вы не собираетесь изменять os.environ для текущего процесса:
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
ценить.
Это стало возможным благодаря ПЭП 448 (Дополнительные обобщения распаковки).
Другой пример.Если у вас есть среда по умолчанию (т. os.environ
), и словарь, с помощью которого вы хотите переопределить значения по умолчанию, вы можете выразить это следующим образом:
my_env = {**os.environ, **dict_with_env_variables}
Чтобы временно установить переменную среды без необходимости копировать объект os.envrion и т. д., я делаю это:
process = subprocess.Popen(['env', 'RSYNC_PASSWORD=foobar', 'rsync', \
'rsync://username@foobar.com::'], stdout=subprocess.PIPE)
Параметр env принимает словарь.Вы можете просто взять os.environ, добавить к нему ключ (нужную вам переменную) (в копию словаря, если необходимо) и использовать его в качестве параметра для Popen
.
Я знаю, что на этот вопрос уже давно был дан ответ, но есть некоторые моменты, которые некоторые могут захотеть узнать об использовании PYTHONPATH вместо PATH в своей переменной среды.Я изложил объяснение запуска скриптов Python с помощью cronjobs, которое по-другому работает с измененной средой (нашел здесь).Думаю, это будет полезно для тех, кому, как и мне, нужно немного больше, чем дал этот ответ.
В определенных обстоятельствах вы можете захотеть передать только те переменные среды, которые нужны вашему подпроцессу, но я думаю, что в целом у вас есть правильная идея (я тоже так делаю).