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

286

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

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

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

Oren_H
источник
10
Также предпочитайте использовать os.pathsepвместо «:» пути, которые работают на разных платформах. См. Stackoverflow.com/questions/1499019/…
amit
8
@phaedrus Я не уверен, что это очень актуально, когда он использует такие пути, как /usr/sbin:-)
Дмитрий Гинзбург

Ответы:

407

Я думаю, что 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)
Даниэль Берк
источник
>>> env = os.environ.copy >>> env ['foo'] = 'bar' Traceback (последний вызов был последним): файл "<stdin>", строка 1, в <module> TypeError: 'instancemethod' объект не поддерживает назначение элемента
user1338062
5
@ user1338062 Вы присваиваете фактический метод os.environ.copyк envпеременной , но вы должны присвоить результат вызова метода os.environ.copy()к env.
Chown
4
Разрешение переменной среды действительно работает, только если вы используете его shell=Trueв своем subprocess.Popenвызове. Обратите внимание, что это может иметь последствия для безопасности.
danielpops
Внутри подпроцесса. Открыть (my_command, env = my_env) - что такое "my_command"
avinash
@avinash - my_commandэто просто команда для запуска. Это может быть, например, /path/to/your/own/programили любой другой «исполняемый» оператор.
kajakIYD
65

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

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

skyking
источник
4
upvote. Я не знал, что ты можешь написать dict(mapping, **kwargs). Я думал, что это было или. Примечание: он копируется os.environбез изменения, как предложено @Daniel Burke в принятом в настоящее время ответе, но ваш ответ более лаконичен. В Python 3.5+ вы могли бы даже сделать dict(**{'x': 1}, y=2, **{'z': 3}). Смотрите Пеп 448 .
Jfs
1
Этот ответ объясняет несколько лучших способов (и почему этот способ не так
хорош
@krupan: какой недостаток вы видите для этого конкретного варианта использования? (Объединение произвольных диктовок и копирование / обновление среды - разные задачи).
JFS
1
@krupan Прежде всего, нормальный случай - переменные окружения будут действительными идентификаторами python, что означает первую конструкцию. В этом случае ни одно из ваших возражений не обосновано. Во втором случае ваше основное возражение все еще не выполнено: пункт о нестроковых ключах в этом случае неприменим, так как ключи в любом случае должны быть строками в среде.
скайкинг
@JFSebastian Вы правы, что для этого конкретного случая этот метод подходит, и я должен был объяснить себя лучше. Мои извинения. Я просто хотел помочь тем (таким как я), которые могли испытать искушение взять эту технику и применить ее к общему случаю слияния двух произвольных словарей (у которых есть некоторые ошибки, как я указал в ответе).
Крупан
24

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

SilentGhost
источник
22

С 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}
Сковородкин
источник
@avinash, ознакомьтесь с подпроцессом . Откройте документацию. Это «последовательность аргументов программы или отдельная строка».
сковородкин
10

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

process = subprocess.Popen(['env', 'RSYNC_PASSWORD=foobar', 'rsync', \
'rsync://username@foobar.com::'], stdout=subprocess.PIPE)
MFB
источник
4

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

Нуфал Ибрагим
источник
Это самый простой ответ, если вы просто хотите добавить новую переменную среды. os.environ['SOMEVAR'] = 'SOMEVAL'
Энди Фрэйли
1

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

derigible
источник
0

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

Эндрю Айлетт
источник