Podproces języka Python / Popen ze zmodyfikowanym środowiskiem

285

Uważam, że uruchamianie zewnętrznego polecenia w nieco zmodyfikowanym środowisku jest bardzo częstym przypadkiem. Tak zazwyczaj to robię:

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

Mam przeczucie, że jest lepszy sposób; czy to wygląda dobrze?

Oren_H
źródło
10
Wolę także używać os.pathsepzamiast „:” dla ścieżek, które działają na różnych platformach. Zobacz stackoverflow.com/questions/1499019/…
amit
8
@phaedrus Nie jestem pewien, czy to bardzo istotne, gdy używa ścieżek takich jak /usr/sbin:-)
Dmitry Ginzburg,

Odpowiedzi:

405

Myślę, że os.environ.copy()lepiej, jeśli nie zamierzasz modyfikować os.environ dla bieżącego procesu:

import subprocess, os
my_env = os.environ.copy()
my_env["PATH"] = "/usr/sbin:/sbin:" + my_env["PATH"]
subprocess.Popen(my_command, env=my_env)
Daniel Burke
źródło
>>> env = os.environ.copy >>> env ['foo'] = 'bar' Traceback (ostatnie ostatnie połączenie): Plik „<stdin>”, wiersz 1, w <module> TypeError: 'instancemethod' obiekt nie obsługuje przypisywania przedmiotów
1338062,
5
@ user1338062 Przypisujesz rzeczywistą metodę os.environ.copydo envzmiennej, ale musisz przypisać wynik wywołania metody os.environ.copy()do env.
chown
4
Rozdzielczość zmiennych środowiskowych faktycznie działa tylko wtedy, gdy używasz jej shell=Truew subprocess.Popenwywołaniu. Pamiętaj, że może to mieć wpływ na bezpieczeństwo.
danielpops
Wewnątrz subprocess.Popen (my_command ENV = my_env) - co to jest "my_command"
Avinash
@avinash - my_commandto po prostu polecenie do uruchomienia. Może to być na przykład /path/to/your/own/programdowolna inna instrukcja „wykonywalna”.
kajakIYD
64

To zależy od problemu. Aby sklonować i zmodyfikować środowisko, jednym rozwiązaniem może być:

subprocess.Popen(my_command, env=dict(os.environ, PATH="path"))

Ale to w pewnym stopniu zależy od tego, czy zmienione zmienne są poprawnymi identyfikatorami python, którymi najczęściej są (jak często natrafiasz na nazwy zmiennych środowiskowych, które nie są alfanumeryczne + podkreślenie lub zmienne rozpoczynające się od liczby?).

W przeciwnym razie możesz napisać coś takiego:

subprocess.Popen(my_command, env=dict(os.environ, 
                                      **{"Not valid python name":"value"}))

W bardzo dziwnym przypadku (jak często używasz kodów sterujących lub znaków innych niż ascii w nazwach zmiennych środowiskowych?), Że klucze środowiska bytesnie mogą (na python3) nawet używać tej konstrukcji.

Jak widać techniki (szczególnie pierwsze) zastosowane tutaj korzyści na kluczach środowiska są zwykle poprawnymi identyfikatorami python, a także znanymi z góry (w czasie kodowania), drugie podejście ma problemy. W przypadkach, w których tak nie jest, prawdopodobnie powinieneś poszukać innego podejścia .

Król nieba
źródło
3
głosować. Nie wiedziałem, że umiesz pisać dict(mapping, **kwargs). Myślałem, że to albo. Uwaga: kopiuje się os.environbez modyfikacji, jak sugerował @Daniel Burke w aktualnie akceptowanej odpowiedzi, ale twoja odpowiedź jest bardziej zwięzła. W Python 3.5+ możesz to zrobić dict(**{'x': 1}, y=2, **{'z': 3}). Zobacz pep 448 .
jfs
1
Ta odpowiedź wyjaśnia kilka lepszych sposobów (i dlaczego ten sposób nie jest tak świetny), aby połączyć dwa słowniki w jeden nowy: stackoverflow.com/a/26853961/27729
krupan
@krupan: jaką wadę widzisz w tym konkretnym przypadku użycia? (łączenie dowolnych nagrań i kopiowanie / aktualizowanie środowiska to różne zadania).
jfs
1
@krupan Przede wszystkim normalnym przypadkiem jest to, że zmienne środowiskowe byłyby poprawnymi identyfikatorami python, co oznacza pierwszą konstrukcję. W takim przypadku żaden z twoich zastrzeżeń nie ma miejsca. W drugim przypadku twój główny sprzeciw nadal zawodzi: punkt dotyczący kluczy nieciągłych nie ma w tym przypadku zastosowania, ponieważ klucze i tak są zasadniczo wymagane jako ciągi znaków w środowisku.
skyking
@JFSebastian Masz rację, że w tym konkretnym przypadku ta technika jest w porządku i powinienem był się lepiej wytłumaczyć. Przepraszam. Chciałem tylko pomóc tym (takim jak ja), którzy mogliby ulec pokusie, aby zastosować tę technikę i zastosować ją w ogólnym przypadku połączenia dwóch dowolnych słowników (na które mam kilka gotcha, jak wskazałem przywołałem).
krupan
24

możesz użyć my_env.get("PATH", '')zamiast, my_env["PATH"]jeśli w PATHjakiś sposób nie jest zdefiniowany w oryginalnym środowisku, ale poza tym wygląda dobrze.

SilentGhost
źródło
21

Z Python 3.5 możesz to zrobić w ten sposób:

import os
import subprocess

my_env = {**os.environ, 'PATH': '/usr/sbin:/sbin:' + os.environ['PATH']}

subprocess.Popen(my_command, env=my_env)

W tym przypadku otrzymujemy kopię os.environi przesłoniętą PATHwartość.

Było to możliwe dzięki PEP 448 (dodatkowe uogólnienia dotyczące rozpakowywania).

Inny przykład. Jeśli masz domyślne środowisko (tj. os.environ) I słownik, który chcesz zastąpić domyślnymi, możesz to wyrazić w następujący sposób:

my_env = {**os.environ, **dict_with_env_variables}
Skovorodkin
źródło
@avinash, sprawdź podproces . otwórz dokumentację. Jest to „sekwencja argumentów programu lub pojedynczy ciąg znaków”.
skovorodkin
10

Aby tymczasowo ustawić zmienną środowiskową bez konieczności kopiowania obiektu os.envrion itp., Robię to:

process = subprocess.Popen(['env', 'RSYNC_PASSWORD=foobar', 'rsync', \
'rsync://[email protected]::'], stdout=subprocess.PIPE)
MFB
źródło
4

Parametr env akceptuje słownik. Możesz po prostu wziąć os.environ, dodać klucz (pożądaną zmienną) (do kopii nagrania, jeśli musisz) i użyć go jako parametru Popen.

Noufal Ibrahim
źródło
To najprostsza odpowiedź, jeśli chcesz tylko dodać nową zmienną środowiskową. os.environ['SOMEVAR'] = 'SOMEVAL'
Andy Fraley,
1

Wiem, że odpowiedziano na to od pewnego czasu, ale są pewne kwestie, o których niektórzy mogą chcieć wiedzieć o używaniu PYTHONPATH zamiast PATH w zmiennej środowiskowej. Przedstawiłem wyjaśnienie dotyczące uruchamiania skryptów python z cronjobs, które dotyczą zmodyfikowanego środowiska w inny sposób ( tutaj ). Pomyślałem, że przydałoby się to tym, którzy tak jak ja potrzebowali nieco więcej niż ta odpowiedź.

kwalifikowalne
źródło
0

W niektórych okolicznościach możesz chcieć przekazać tylko te zmienne środowiskowe, których potrzebuje Twój podproces, ale myślę, że ogólnie masz dobry pomysł (tak też robię).

Andrew Aylett
źródło