У меня есть сценарий, который выглядит примерно так:
export foo=/tmp/foo
export bar=/tmp/bar
Каждый раз, когда я создаю, я запускаю 'source init_env' (где init_env - это сценарий выше), чтобы установить некоторые переменные.
Чтобы сделать то же самое в Python, у меня был запущен этот код,
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)
Но потом кто-то решил, что было бы неплохо добавить в init_env
файл такую строку:
export PATH="/foo/bar:/bar/foo:$PATH"
Очевидно, мой скрипт Python развалился. Я мог бы изменить скрипт Python для обработки этой строки, но потом он просто сломается, когда кто-нибудь предложит новую функцию для использования в init_env
файле.
Вопрос в том, есть ли простой способ запустить команду Bash и позволить ей изменить мою os.environ
?
Ответы:
Проблема с вашим подходом в том, что вы пытаетесь интерпретировать сценарии bash. Сначала вы просто пытаетесь интерпретировать оператор экспорта. Затем вы замечаете, что люди используют расширение переменных. Позже люди будут помещать в свои файлы условные обозначения или обрабатывать замены. В конце концов, у вас будет полноценный интерпретатор сценариев bash с кучей ошибок. Не делай этого.
Пусть Bash интерпретирует файл за вас, а затем соберет результаты.
Сделать это можно так:
#! /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))
Убедитесь, что вы обрабатываете ошибки в случае, если bash не работает
source init_env
, или сам bash не может выполняться, или подпроцесс не может выполнить bash, или любые другие ошибки.env -i
в начале командной строки создает чистую окружающую среду. это означает, что вы получите только переменные среды изinit_env
. если вам нужна унаследованная системная среда, опуститеenv -i
.Прочтите документацию по подпроцессу для получения более подробной информации.
Примечание: это будет захватывать только переменные, установленные с помощью
export
оператора, посколькуenv
печатает только экспортированные переменные.Наслаждаться.
Обратите внимание, что в документации Python говорится, что если вы хотите манипулировать средой, вы должны манипулировать
os.environ
напрямую, а не использоватьos.putenv()
. Я считаю это ошибкой, но я отвлекся.источник
proc.stdout()
урожайности байт, таким образом , я получаюTypeError
наline.partition()
. Преобразование в строку сline.decode().partition("=")
решением проблемы.['env', '-i', 'bash', '-c', 'source .bashrc && env']
чтобы предоставить себе только переменные среды, установленные файлом rcИспользование рассола:
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
Обновлено:
Это использует json, subprocess и явно использует / bin / bash (для поддержки 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
источник
/bin/dash
, которая не знаетsource
команды. Чтобы использовать его в Ubuntu, вы должны запустить его/bin/bash
явно, например, используяpenv = subprocess.Popen(['/bin/bash', '-c', '%s && %s' %(source,dump)], stdout=subprocess.PIPE).stdout
(здесь используется более новыйsubprocess
модуль, который необходимо импортировать).Вместо того, чтобы иметь в качестве источника сценария Python сценарий bash, было бы проще и элегантнее иметь источник сценария-оболочки,
init_env
а затем запускать сценарий Python в измененной среде.#!/bin/bash source init_env /run/python/script.py
источник
bash --rcfile init_env -c ./script.py
Обновлен ответ @ lesmana для Python 3. Обратите внимание, что использование этого параметра
env -i
предотвращает установку / сброс посторонних переменных среды (возможно, неправильно, учитывая отсутствие обработки многострочных переменных 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
источник
Пример упаковки отличного ответа @Brian в функцию:
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())
Я использую эту служебную функцию для чтения учетных данных aws и файлов docker .env с помощью
include_unexported_variables=True
.источник