在Python中模拟Bash“源代码”
我有一个如下所示的脚本:
export foo=/tmp/foo export bar=/tmp/bar
每次我build立我运行'源init_env'(其中init_env是上述脚本)设置一些variables。
为了在Python中完成相同的function,我运行了这段代码,
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脚本。 首先,你只是试图解释出口声明。 然后你注意到人们正在使用可变的扩展。 后来人们会把条件放在他们的文件中,或者进行replace。 最后,你将有一个完整的bash脚本解释器,一个巨大的bug。 不要这样做。
让Bash为你解释文件,然后收集结果。
你可以这样做:
#! /usr/bin/env python import os import pprint import subprocess command = ['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本身无法执行,或者子source init_env
无法执行bash或其他错误,请确保处理错误。
阅读有关子stream程的文档以获取更多详细信息。
注意:这将只捕获使用export
语句设置的variables,因为env
只打印导出的variables。
请享用。
请注意, Python文档说,如果你想操纵环境,你应该直接操作os.environ
而不是使用os.putenv()
。 我认为这是一个错误,但我离题了。
使用泡菜:
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
有一个包装脚本源init_env
,然后用修改后的环境运行你的Python脚本,而不是让你的Python脚本来源bash脚本,它会更简单,更优雅。
#!/bin/bash source init_env /run/python/script.py
更新了@ lesmana对于Python 3的答案。请注意,使用env -i
可以防止无关的环境variables被设置/重置(可能错误地给出了多行envvariables缺less处理的问题)。
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
。
execfile('filename')
应该这样做