Подпроцесс Python / Popen с измененной средой - PullRequest
242 голосов
/ 09 февраля 2010

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

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

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

Ответы [ 8 ]

339 голосов
/ 15 декабря 2010

Я думаю, 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)
56 голосов
/ 11 марта 2015

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

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

23 голосов
/ 09 февраля 2010

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

14 голосов
/ 24 мая 2016

С 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}
8 голосов
/ 07 сентября 2016

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

process = subprocess.Popen(['env', 'RSYNC_PASSWORD=foobar', 'rsync', \
'rsync://username@foobar.com::'], stdout=subprocess.PIPE)
2 голосов
/ 09 февраля 2010

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

1 голос
/ 17 июля 2013

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

0 голосов
/ 09 февраля 2010

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

...