我有一个文本文件
/etc/default/foo
,其中包含一行:
FOO="/path/to/foo"
在我的Python脚本中,我需要引用变量FOO。
将文件
/etc/default/foo
“源”到我的 python 脚本中的最简单方法是什么,就像我在 bash 中所做的那样?
. /etc/default/foo
与 @jil 的答案相同,但是该答案特定于 Python 的某些历史版本。
在现代 Python (3.x) 中:
exec(open('filename').read())
替换 2.x
中的
execfile('filename')
它可以与上下文管理器一起使用,如 MartinUeding 提到的,以确保文件关闭:
with open('filename') as f:
exec(f.read())
您可以使用execfile:
execfile("/etc/default/foo")
但请注意,这将按原样评估文件的内容到您的程序源中。除非您完全信任来源,否则这是潜在的安全隐患。
这也意味着该文件需要是有效的 python 语法(您给定的示例文件是)。
请记住,如果您有一个包含此内容且文件扩展名为 .py 的“文本”文件,您始终可以执行以下操作:
import mytextfile
print(mytestfile.FOO)
当然,就 Python 而言,这假设文本文件在语法上是正确的。在我参与的一个项目中,我们做了类似的事情。将一些文本文件转换为 Python 文件。古怪但也许值得考虑。
如果您确定它只包含
VAR="QUOTED STRING"
样式变量,如下所示:
FOO="some value"
那么你可以这样做:
>>> with open('foo.sysconfig') as fd:
... exec(fd.read())
这让你:
>>> FOO
'some value'
(这实际上与
execfile()
解决方案相同
在其他答案中建议。)
此方法具有重大安全隐患;如果您的文件包含:
而不是
FOO="some value"
os.system("rm -rf /")
那么你就有麻烦了。
或者,您可以这样做:
>>> with open('foo.sysconfig') as fd:
... settings = {var: shlex.split(value) for var, value in [line.split('=', 1) for line in fd]}
这会给你一本字典
settings
,里面有:
>>> settings
{'FOO': ['some value']}
那
settings = {...}
行正在使用字典理解。您可以使用 for
循环等在几行中完成同样的事情。
当然,如果文件包含像
${somevar:-value_if_not_set}
这样的 shell 样式变量扩展,那么这将不起作用(除非您编写自己的 shell 样式变量解析器)。
只是为了提供不同的方法,请注意,如果您的原始文件设置为
export FOO=/path/to/foo
您可以执行
source /etc/default/foo; python myprogram.py
(或. /etc/default/foo; python myprogram.py
),并且在myprogram.py
内,源文件中导出的所有值都在os.environ中可见,例如
import os
os.environ["FOO"]
有几种方法可以做到这一点。
你确实可以
import
将该文件作为一个模块,只要它包含的数据符合Python的语法即可。但是,要么有问题的文件是与脚本位于同一目录中的 .py
,要么您要使用 imp
(或 importlib
,具体取决于您的版本)就像这里。另一种解决方案(我的偏好)可以是使用任何 python 库都可以解析的数据格式(我想到的是 JSON 作为示例)。
/etc/default/foo:
{"FOO":"path/to/foo"}
在你的Python代码中:
import json
with open('/etc/default/foo') as file:
data = json.load(file)
FOO = data["FOO"]
## ...
file.close()
这样,您就不会冒险执行一些不确定的代码...
您可以选择,具体取决于您的喜好。如果您的数据文件是由某些脚本自动生成的,那么保留像
FOO="path/to/foo"
这样的简单语法并使用 imp
可能会更容易。
希望对您有帮助!
这是我的方法:自己解析 bash 文件并仅处理变量赋值行,例如:
FOO="/path/to/foo"
这是代码:
import shlex
def parse_shell_var(line):
"""
Parse such lines as:
FOO="My variable foo"
:return: a tuple of var name and var value, such as
('FOO', 'My variable foo')
"""
return shlex.split(line, posix=True)[0].split('=', 1)
if __name__ == '__main__':
with open('shell_vars.sh') as f:
shell_vars = dict(parse_shell_var(line) for line in f if '=' in line)
print(shell_vars)
看一下这个片段:
shell_vars = dict(parse_shell_var(line) for line in f if '=' in line)
此行迭代 shell 脚本中的行,仅处理具有等号的行(这不是检测变量赋值的万无一失的方法,而是最简单的方法)。接下来,将这些行运行到函数
parse_shell_var
中,该函数使用 shlex.split
来正确处理引号(或缺少引号)。最后,这些片段被组装成一本字典。该脚本的输出是:
{'MOO': '/dont/have/a/cow', 'FOO': 'my variable foo', 'BAR': 'My variable bar'}
这是shell_vars.sh的内容:
FOO='my variable foo'
BAR="My variable bar"
MOO=/dont/have/a/cow
echo $FOO
这种方法有几个优点:
这种方法并不完美,它有一些局限性:
它无法正确解析基于其他变量或命令构建的值。这意味着,对于以下行,它将失败:
FOO=$BAR
FOO=$(pwd)
根据 exec(.read()), value = eval(.read()) 的答案,它只会返回值。例如
1 + 1: 2
"Hello Word": "Hello World"
float(2) + 1: 3.0