Підпроцес Python / Popen з модифікованим середовищем


285

Я вважаю, що запуск зовнішньої команди з дещо зміненим середовищем - дуже поширений випадок. Ось як я це прагну:

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

У мене кишка відчуває, що є кращий спосіб; це добре виглядає?


10
Також віддайте перевагу використовувати os.pathsepзамість ":" для шляхів, які працюють на платформах. Див stackoverflow.com/questions/1499019 / ...
Amit

8
@phaedrus Я не впевнений, що це дуже актуально, коли він використовує такі шляхи, як /usr/sbin:-)
Дмитро Гінзбург

Відповіді:


405

Я думаю 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' Відстеження (останній останній дзвінок останній): Файл "<stdin>", рядок 1, в <module> TypeError: 'instancemethod' об'єкт не підтримує призначення елемента
user1338062

5
@ User1338062 Ви привласнюєте фактичний метод os.environ.copyдо envзмінної , але ви повинні привласнити результат виклику методу os.environ.copy()до env.
човен

4
Роздільна здатність змінної середовища фактично працює лише в тому випадку, якщо ви використовуєте shell=Trueдля subprocess.Popenвиклику. Зауважте, що це може мати наслідки для безпеки.
danielpops

Усередині subprocess.Popen (my_command, окр = my_env) - що "my_command"
Авинаш

@avinash - my_commandце просто команда для запуску. Це може бути, наприклад, /path/to/your/own/programбудь-яка інша "виконана" заява.
kajakIYD

64

Це залежить від проблеми. Якщо це клонувати та змінювати середовище, одним із рішень може бути:

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) навіть використовувати цю конструкцію.

Як ви бачите, використовувані тут методи (особливо перші), переваги на клавішах середовища, як правило, є дійсними ідентифікаторами пітона, а також відомими заздалегідь (під час кодування), другий підхід має проблеми. У випадках, коли це не так, вам, мабуть, слід шукати інший підхід .


3
піднести Я не знав, що ти можеш писати dict(mapping, **kwargs). Я думав, що це або. Примітка: він копіює, os.environне змінюючи його, як @Daniel Burke запропоновано у відповіді, що прийнято, але ваша відповідь є більш короткою. У Python 3.5+ ви навіть можете це зробити dict(**{'x': 1}, y=2, **{'z': 3}). Див. Pep 448 .
jfs

1
Ця відповідь пояснює деякі більш ефективні способи (і чому цей спосіб не такий великий) , щоб об'єднати два словника в один новий: stackoverflow.com/a/26853961/27729
krupan

@krupan: який недолік ви бачите для цього конкретного випадку використання? (об'єднання довільних диктів та копіювання / оновлення середовища - це різні завдання).
jfs

1
@krupan Перш за все, нормальний випадок полягає в тому, що змінні середовища будуть дійсними ідентифікаторами python, що означає першу конструкцію. У цьому випадку жодне з ваших заперечень не відповідає. У другому випадку ваше основне заперечення все-таки не вдається: пункт про нестрокові ключі в даному випадку не застосовується, оскільки в основному ключі повинні бути рядками в оточенні.
злетіння

@JFSebastian Ви вірні, що для цього конкретного випадку ця техніка прекрасна, і я повинен був пояснити себе краще. Мої вибачення. Я просто хотів допомогти тим, (як я), хто міг би спокусити застосувати цю методику і застосувати її до загального випадку злиття двох довільних словників (у яких є деякі готчі, як відповідь, на яку я зазначив).
крупан

24

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


21

З 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)

4

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


Це найпростіша відповідь, якщо ви просто хочете додати нову змінну середовища. os.environ['SOMEVAR'] = 'SOMEVAL'
Енді Фралі

1

Я знаю, що на це відповідали вже деякий час, але є деякі моменти, які, можливо, деякі хочуть знати про використання PYTHONPATH замість PATH у змінній їх середовища. Я окреслив пояснення запуску сценаріїв python з cronjobs, який по-іншому стосується модифікованого середовища ( знайдено тут ). Думав, що це буде корисно для тих, хто, як я, потребував трохи більше, ніж ця відповідь.


0

За певних обставин ви можете лише передати змінні середовища, якими потрібні ваші підпроцеси, але я думаю, що ви маєте правильне уявлення загалом (саме так я і роблю).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.