如何将文件“源”到Python脚本中

问题描述 投票:0回答:8

我有一个文本文件

/etc/default/foo
,其中包含一行:

FOO="/path/to/foo"

在我的Python脚本中,我需要引用变量FOO。

将文件

/etc/default/foo
“源”到我的 python 脚本中的最简单方法是什么,就像我在 bash 中所做的那样?

. /etc/default/foo
python bash python-2.7
8个回答
37
投票

与 @jil 的答案相同,但是该答案特定于 Python 的某些历史版本。

在现代 Python (3.x) 中:

exec(open('filename').read())

替换 2.x

 中的 
execfile('filename')

它可以与上下文管理器一起使用,如 MartinUeding 提到的,以确保文件关闭:

with open('filename') as f:
    exec(f.read())

20
投票

您可以使用execfile

execfile("/etc/default/foo")

但请注意,这将按原样评估文件的内容到您的程序源中。除非您完全信任来源,否则这是潜在的安全隐患。

这也意味着该文件需要是有效的 python 语法(您给定的示例文件是)。


10
投票

请记住,如果您有一个包含此内容且文件扩展名为 .py 的“文本”文件,您始终可以执行以下操作:

import mytextfile

print(mytestfile.FOO)

当然,就 Python 而言,这假设文本文件在语法上是正确的。在我参与的一个项目中,我们做了类似的事情。将一些文本文件转换为 Python 文件。古怪但也许值得考虑。


7
投票

如果您确定它只包含

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 样式变量解析器)。


7
投票

只是为了提供不同的方法,请注意,如果您的原始文件设置为

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"] 

3
投票

有几种方法可以做到这一点。

  • 你确实可以

    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
可能会更容易。

希望对您有帮助!


1
投票

解决方案

这是我的方法:自己解析 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

讨论

这种方法有几个优点:

  • 它不执行 shell(无论是在 bash 中还是在 Python 中),这避免了任何副作用
  • 因此,即使 shell 脚本的来源未知,也可以安全使用
  • 它可以正确处理带或不带引号的值

这种方法并不完美,它有一些局限性:

  • 检测变量赋值的方法(通过查找等号是否存在)是原始且不准确的。有一些方法可以更好地检测这些线条,但这是另一天的主题
  • 它无法正确解析基于其他变量或命令构建的值。这意味着,对于以下行,它将失败:

    FOO=$BAR
    FOO=$(pwd)
    

0
投票

根据 exec(.read()), value = eval(.read()) 的答案,它只会返回值。例如

1 + 1: 2
"Hello Word": "Hello World"
float(2) + 1: 3.0
© www.soinside.com 2019 - 2024. All rights reserved.