Mam skrypt, który wygląda mniej więcej tak:
export foo=/tmp/foo
export bar=/tmp/bar
Za każdym razem, gdy buduję, uruchamiam 'source init_env' (gdzie init_env to powyższy skrypt), aby ustawić kilka zmiennych.
Aby osiągnąć to samo w Pythonie, uruchomiłem ten kod,
reg = re.compile('export (?P<name>\w+)(\=(?P<value>.+))*')
for line in open(file):
m = reg.match(line)
if m:
name = m.group('name')
value = ''
if m.group('value'):
value = m.group('value')
os.putenv(name, value)
Ale potem ktoś zdecydował, że fajnie byłoby dodać do pliku linię podobną do poniższej init_env
:
export PATH="/foo/bar:/bar/foo:$PATH"
Oczywiście mój skrypt w Pythonie się rozpadł. Mógłbym zmodyfikować skrypt Pythona, aby obsłużyć ten wiersz, ale potem po prostu zepsuje się, gdy ktoś wymyśli nową funkcję do użycia w init_env
pliku.
Pytanie brzmi, czy istnieje łatwy sposób na uruchomienie polecenia Bash i pozwolenie mu zmodyfikować moje os.environ
?
Odpowiedzi:
Problem z twoim podejściem polega na tym, że próbujesz interpretować skrypty bash. Najpierw spróbuj zinterpretować oświadczenie eksportowe. Wtedy zauważasz, że ludzie używają rozszerzania zmiennych. Później ludzie będą umieszczać warunki warunkowe w swoich plikach lub podstawiać procesy. W końcu będziesz miał pełny interpreter skryptów basha z gazylionami błędów. Nie rób tego.
Pozwól Bashowi zinterpretować plik za Ciebie, a następnie zbierz wyniki.
Możesz to zrobić w ten sposób:
#! /usr/bin/env python import os import pprint import shlex import subprocess command = shlex.split("env -i bash -c 'source init_env && env'") proc = subprocess.Popen(command, stdout = subprocess.PIPE) for line in proc.stdout: (key, _, value) = line.partition("=") os.environ[key] = value proc.communicate() pprint.pprint(dict(os.environ))
Upewnij się, że obsługujesz błędy w przypadku, gdy bash się nie powiedzie
source init_env
lub sam bash nie może wykonać, lub podproces nie może wykonać bash, lub inne błędy.env -i
na początku wiersza poleceń tworzy czyste środowisko. oznacza to, że będziesz otrzymywać zmienne środowiskowe tylko zinit_env
. jeśli chcesz odziedziczone środowisko systemowe, pomińenv -i
.Przeczytaj dokumentację dotyczącą podprocesu, aby uzyskać więcej informacji.
Uwaga: spowoduje to przechwycenie tylko zmiennych ustawionych w
export
instrukcji, ponieważenv
wypisuje tylko wyeksportowane zmienne.Cieszyć się.
Zauważ, że dokumentacja Pythona mówi, że jeśli chcesz manipulować środowiskiem, powinieneś manipulować
os.environ
bezpośrednio, zamiast używaćos.putenv()
. Uważam to za błąd, ale błądzę.źródło
proc.stdout()
wydajnością bajty, więc ja dostajęTypeError
naline.partition()
. Konwersja na łańcuch zline.decode().partition("=")
rozwiązanym problemem.['env', '-i', 'bash', '-c', 'source .bashrc && env']
, aby dać sobie tylko zmienne środowiskowe określone przez plik rcUżywanie marynaty:
import os, pickle # For clarity, I moved this string out of the command source = 'source init_env' dump = '/usr/bin/python -c "import os,pickle;print pickle.dumps(os.environ)"' penv = os.popen('%s && %s' %(source,dump)) env = pickle.loads(penv.read()) os.environ = env
Zaktualizowano:
Używa json, podprocesu i jawnie używa / bin / bash (do obsługi Ubuntu):
import os, subprocess as sp, json source = 'source init_env' dump = '/usr/bin/python -c "import os, json;print json.dumps(dict(os.environ))"' pipe = sp.Popen(['/bin/bash', '-c', '%s && %s' %(source,dump)], stdout=sp.PIPE) env = json.loads(pipe.stdout.read()) os.environ = env
źródło
/bin/dash
, która nie znasource
polecenia. Aby użyć go na Ubuntu, musisz uruchomić go/bin/bash
jawnie, np. Używającpenv = subprocess.Popen(['/bin/bash', '-c', '%s && %s' %(source,dump)], stdout=subprocess.PIPE).stdout
(to używa nowszegosubprocess
modułu, który trzeba zaimportować).Zamiast mieć źródło skryptu Python jako skrypt bash, prostsze i bardziej eleganckie byłoby posiadanie źródła skryptu opakowującego,
init_env
a następnie uruchamianie skryptu w Pythonie w zmodyfikowanym środowisku.#!/bin/bash source init_env /run/python/script.py
źródło
bash --rcfile init_env -c ./script.py
Zaktualizowana odpowiedź @ lesmana dla Pythona 3. Zwróć uwagę, że użycie tego narzędzia
env -i
zapobiega ustawianiu / resetowaniu zewnętrznych zmiennych środowiskowych (potencjalnie niepoprawnie, biorąc pod uwagę brak obsługi wielowierszowych zmiennych env).import os, subprocess if os.path.isfile("init_env"): command = 'env -i sh -c "source init_env && env"' for line in subprocess.getoutput(command).split("\n"): key, value = line.split("=") os.environ[key]= value
źródło
Przykład zawijania doskonałej odpowiedzi @ Briana w funkcji:
import json import subprocess # returns a dictionary of the environment variables resulting from sourcing a file def env_from_sourcing(file_to_source_path, include_unexported_variables=False): source = '%ssource %s' % ("set -a && " if include_unexported_variables else "", file_to_source_path) dump = '/usr/bin/python -c "import os, json; print json.dumps(dict(os.environ))"' pipe = subprocess.Popen(['/bin/bash', '-c', '%s && %s' % (source, dump)], stdout=subprocess.PIPE) return json.loads(pipe.stdout.read())
Używam tej funkcji narzędzia do odczytywania poświadczeń aws i plików docker .env z plikami
include_unexported_variables=True
.źródło