在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') 

应该这样做