在 Python 中模拟 Bash“源”
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3503719/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Emulating Bash 'source' in Python
提问by getekha
I have a script that looks something like this:
我有一个看起来像这样的脚本:
export foo=/tmp/foo
export bar=/tmp/bar
Every time I build I run 'source init_env' (where init_env is the above script) to set up some variables.
每次构建时,我都会运行“source init_env”(其中 init_env 是上面的脚本)来设置一些变量。
To accomplish the same in Python I had this code running,
为了在 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)
But then someonedecided it would be nice to add a line like the following to the init_envfile:
但是后来有人决定在init_env文件中添加如下一行会很好:
export PATH="/foo/bar:/bar/foo:$PATH"
Obviously my Python script fell apart. I could modify the Python script to handle this line, but then it'll just break later on when someonecomes up with a new feature to use in the init_envfile.
显然我的 Python 脚本崩溃了。我可以修改 Python 脚本来处理这一行,但是当有人提出要在init_env文件中使用的新功能时,它就会中断。
The question is if there is an easy way to run a Bash command and let it modify my os.environ?
问题是是否有一种简单的方法来运行 Bash 命令并让它修改我的os.environ?
采纳答案by lesmana
The problem with your approach is that you are trying to interpret bash scripts. First you just try to interpret the export statement. Then you notice people are using variable expansion. Later people will put conditionals in their files, or process substitutions. In the end you will have a full blown bash script interpreter with a gazillion bugs. Don't do that.
您的方法的问题在于您正在尝试解释 bash 脚本。首先,您只是尝试解释导出语句。然后你注意到人们正在使用变量扩展。稍后人们会将条件放在他们的文件中,或处理替换。最后,您将拥有一个完整的 bash 脚本解释器,其中包含大量错误。不要那样做。
Let Bash interpret the file for you and then collect the results.
让 Bash 为您解释文件,然后收集结果。
You can do it like this:
你可以这样做:
#! /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))
Make sure that you handle errors in case bash fails to source init_env, or bash itself fails to execute, or subprocess fails to execute bash, or any other errors.
确保在 bash 无法source init_env执行、bash 本身无法执行、子进程无法执行 bash 或任何其他错误的情况下处理错误。
the env -iat the beginning of the command line creates a clean environment. that means you will only get the environment variables from init_env. if you want the inherited system environment then omit env -i.
将env -i在命令行的开始创造一个清洁的环境。这意味着您只能从init_env. 如果您想要继承的系统环境,则省略env -i.
Read the documentation on subprocessfor more details.
阅读关于子流程的文档以获取更多详细信息。
Note: this will only capture variables set with the exportstatement, as envonly prints exported variables.
注意:这只会捕获用export语句设置的变量,因为env只打印导出的变量。
Enjoy.
享受。
Note that the Python documentationsays that if you want to manipulate the environment you should manipulate os.environdirectly instead of using os.putenv(). I consider that a bug, but I digress.
请注意,Python 文档说如果你想操作环境,你应该os.environ直接操作而不是使用os.putenv(). 我认为这是一个错误,但我离题了。
回答by John Kugelman
Rather than having your Python script source the bash script, it would be simpler and more elegant to have a wrapper script source init_envand then run your Python script with the modified environment.
与其让您的 Python 脚本作为 bash 脚本的源代码,不如拥有一个包装脚本源代码init_env,然后在修改后的环境中运行您的 Python 脚本会更简单、更优雅。
#!/bin/bash
source init_env
/run/python/script.py
回答by Brian
Using pickle:
使用泡菜:
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
Updated:
更新:
This uses json, subprocess, and explicitly uses /bin/bash (for ubuntu support):
这使用 json、子进程,并显式使用 /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
回答by Al Johri
Updated @lesmana's answer for Python 3. Notice the use of env -iwhich prevents extraneous environment variables from being set/reset (potentially incorrectly given the lack of handling for multiline env variables).
更新了 @lesmana 对 Python 3 的回答。请注意使用env -iwhich 可以防止设置/重置无关的环境变量(由于缺乏对多行环境变量的处理,可能是错误的)。
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
回答by JDiMatteo
Example wrapping @Brian's excellent answer in a function:
将@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())
I'm using this utility function to read aws credentials and docker .env files with include_unexported_variables=True.
我正在使用此实用程序函数读取 aws 凭据和 docker .env 文件include_unexported_variables=True。

