如何在要使用python脚本解析的yaml文件中替换环境变量值

npa*_*tel 6 python pyyaml python-2.7

我需要在yaml文件中使用环境变量“ PATH”,该环境变量需要使用脚本进行解析。

这是我在终端上设置的环境变量:

$ echo $PATH
/Users/abc/Downloads/tbwork
Run Code Online (Sandbox Code Playgroud)

这是我的sample.yml:

---
Top: ${PATH}/my.txt
Vars:
- a
- b
Run Code Online (Sandbox Code Playgroud)

当我用脚本解析该Yaml文件时,看不到PATH变量的实际值。

这是我的脚本:

import yaml
import os
import sys

stream = open("sample.yml", "r")
docs = yaml.load_all(stream)
for doc in docs:
    for k,v in doc.items():
        print k, "->", v
    print "\n",
Run Code Online (Sandbox Code Playgroud)

输出:

Top -> ${PATH}/my.txt
Vars -> ['a', 'b']
Run Code Online (Sandbox Code Playgroud)

预期输出为:

Top -> /Users/abc/Downloads/tbwork/my.txt
Vars -> ['a', 'b']
Run Code Online (Sandbox Code Playgroud)

如果我做错了事,有人可以帮我弄清楚做事的正确方法吗?

dmc*_*chk 9

PY-yaml库默认情况下不解析环境变量。您需要定义一个隐式解析器,该解析器将找到定义环境变量的正则表达式并执行函数来对其进行解析。

您可以通过yaml.add_implicit_resolver和完成此操作yaml.add_constructor。在下面的代码中,您将定义一个解析器,该解析器将与YAML值中的$ {env variable}匹配,并调用函数path_constructor来查找环境变量。

import yaml
import re
import os

path_matcher = re.compile(r'\$\{([^}^{]+)\}')
def path_constructor(loader, node):
  ''' Extract the matched value, expand env variable, and replace the match '''
  value = node.value
  match = path_matcher.match(value)
  env_var = match.group()[2:-1]
  return os.environ.get(env_var) + value[match.end():]

yaml.add_implicit_resolver('!path', path_matcher)
yaml.add_constructor('!path', path_constructor)

data = """
env: ${VAR}/file.txt
other: file.txt
"""

if __name__ == '__main__':
  p = yaml.safe_load(data)
  print(os.environ.get('VAR')) ## /home/abc
  print(p['env']) ## /home/abc/file.txt
Run Code Online (Sandbox Code Playgroud)

  • 当没有必要(几乎总是)时,永远不要建议使用“load()”(如果您不知道为什么,请阅读文档)。在示例中始终使用“safe_load()”。 (2认同)
  • 要使用 `safe_load()` 我必须像这样修改上面的内容... `yaml.add_implicit_resolver('!path', path_matcher, None, SafeLoader)` `yaml.add_constructor('!path', path_constructor, SafeLoader)` (2认同)
  • 我遇到了与 @dmchk 相同的问题,并使用: `yaml.add_implicit_resolver('!path', path_matcher)` `yaml.add_constructor('!path', path_constructor)` 或: `yaml.add_implicit_resolver('!path' , path_matcher, None, SafeLoader)` `yaml.add_constructor('!path', path_constructor, SafeLoader)` 我无法解决这个问题 (2认同)

kol*_*lis 5

如果您不想修改全局/默认yaml Loader,则这是一个替代版本,它使用新的Loader类。

更重要的是,它可以正确替换不只是环境变量的内插字符串,例如 path/to/${SOME_VAR}/and/${NEXT_VAR}/foo/bar

        path_matcher = re.compile(r'.*\$\{([^}^{]+)\}.*')
        def path_constructor(loader, node):
            return os.path.expandvars(node.value)

        class EnvVarLoader(yaml.SafeLoader):
            pass

        EnvVarLoader.add_implicit_resolver('!path', path_matcher, None)
        EnvVarLoader.add_constructor('!path', path_constructor)

        with open(configPath) as f:
            c = yaml.load(f, Loader=EnvVarLoader)
Run Code Online (Sandbox Code Playgroud)


小智 5

有一个很好的库envyaml为此。有了它,它非常简单:

from envyaml import EnvYAML

# read file env.yaml and parse config
env = EnvYAML('env.yaml')
Run Code Online (Sandbox Code Playgroud)

  • 可能还有更多内容,但看来这个库的主要方法是这个巧妙的小技巧:`yaml = safe_load(os.path.expandvars(f.read()))` (3认同)