【问题标题】:combine multiple yaml files with indentation in python在python中将多个yaml文件与缩进结合起来
【发布时间】:2018-07-11 17:21:20
【问题描述】:

我有以下目录和文件结构。我想从以下这些文件夹中的所有 yaml 文件中创建一个 yaml 文件。

[root@localhost test]# tree
.
├── group_vars
│   └── all.template
├── host_vars
│   └── host.template
└── vars
    ├── CASSANDRA
    ├── CQLSH
    ├── CSYNC2
    ├── DSE_OPSCENTER
    ├── DSE_OPSCENTER_AGENT
    ├── logging.template
    ├── packages_vars.template
    ├── UDM
    └── user_pub_keys

结果可能是(示例 yaml 文件)

group_vars/all.template:
    <all the all.template data at this indentation>
host_vars/host.template:
    <all the host.template data at this indentation>
vars/CASSANDRA:
    <all the CASSANDRA data at this indentation>
vars/CQLSH:
    <all the CQLSH data at this indentation>
... so  on

我可以在文件夹中加入这些文件,但我不知道如何才能带来我上面描述的 yaml 格式。

我尝试了什么?

我想到了写入文件&lt;folder_name&gt;/file_name&gt;,然后给4个空格并按原样写入内容。

类似下面的东西

with open(actual_path) as i: # actual path is just the path to the file
    outfile.write('vars#'+fname) # vars is the folder name and fname is the file name. # is just any separator for the file
    outfile.write(i.read()) # here I can add 4 spaces 
    outfile.write('\n')

这是按照我想要的方式创建 yaml 文件的好方法吗?如果是这样,我只需要知道如何在 4 个空格之后开始写入文件(原样)。

【问题讨论】:

    标签: python yaml ruamel.yaml


    【解决方案1】:

    您不能只将文件的内容转储到 YAML 文档(或多个文档)中,因为在加载该内容时会被解析。此类解析的内容可能是不正确的 YAML,导致加载程序错误,或者可能是正确的 YAML,导致数据结构不太可能完全转换为读取的原始文件的(字符串)内容。后者是因为 YAML 转储程序会规范缩进,并且大多数转储程序会处理行尾 cmets。

    文件也可以由二进制数据组成,需要正确编码,或者根据文件的内容进行转义。

    如果任何路径或文件的名称中包含有效的文件名字符,那么将#(如您的代码中的代码)作为路径元素和文件名之间的分隔符的方法将不起作用。您应该使用保留字符(在类似 Unix 的 NUL 字符或 / 上),或者通过将路径拆分为段并将这些段加上文件名放在字符串标量序列中来使内容更易于传输。

    要使所有这些都成为正确的 YAML,请确保将这些信息加载到数据结构中,然后使用 YAML 加载器/转储器库转储该数据结构,而不是尝试自己编写文件。使用 Python,您唯一真正的选择是 ruamel.yaml(免责声明:我是该软件包的作者),例如较旧的 PyYAML 不能将序列转储为映射的键,尽管根据 YAML 规范这是完全有效的。

    创建数据结构的方法有多种,您还需要确定您的文件是包含一个 YAML 文档还是包含多个 YAML 文档。 如果您想要一个文档,我会将由/ 分隔的路径+文件名表示为映射的键,并将文本块标量形式的文件内容作为这些键的值:

    import os
    import sys
    import ruamel.yaml
    
    root_dir = '.'
    
    data = ruamel.yaml.comments.CommentedMap()
    
    for root, directory_names, file_names in os.walk(root_dir):
        if root == root_dir:
            # don't do the file in the current directory, only the ones in subdirs
            continue
        # this makes a list after removing the root_dir
        rsplit = root.replace(root_dir + os.sep, '', 1).split(os.sep)
        for file_name in file_names:
            # open as binary
            with open(os.path.join(root, file_name), 'rb') as fp:
                raw_content = fp.read()
            # then if conversion to unicode fails, keep as binary
            try:
                content = ruamel.yaml.scalarstring.PreservedScalarString(raw_content.decode('utf-8'))
            except UnicodeDecodeError:
                content = raw_content
            # in the next line join the segments using '/', don't use os.sep, as you might
            # not be on Unix/Linux
            data['/'.join(rsplit + [file_name])] = content
    
    yaml = ruamel.yaml.YAML()
    yaml.dump(data, sys.stdout)
    

    给出:

    host_vars/host.template: |+
      this is the content of the file 
      host.template it has two empty lines at the end
    
    
    group_vars/all.template: |
      this is the content of the
      file all.template
    vars/CASSANDRA: !!binary |
      jA0EAwMCeujy0iby+oFgyUiXUDg2VWaphMZSwDxIIyo0h/aVkrmVaRJy7DFjLhfNrKZL9wRiztvL
      slM0cA/N1jDZ2DJCT5317mlTNuWZCoj/8EzvPegpi7w=
    

    程序中除了cmets还有几点需要注意:

    1. CASSANDRA 文件是故意制作的二进制文件(以文件名作为密钥的 gpg 编码是文件 all.template 内容的多次键)。获取!!binary 标签不需要做任何特别的事情。
    2. host.template 末尾有两个空行,因此 YAML 会自动将其转储为 |+
    3. 在读取文件时,如果可移植性很重要,请确保通过使用/ 拆分然后使用os.sep 组合来重构路径+文件名。

    【讨论】:

    • 用你的最后一行,我用 open('config.yaml', 'w') as file: yaml.dump(data, file) 将数据转储到文件中
    • 是的,这就是这样做的方法,在测试时我通常会写信给stdout,所以我可以看到发生了什么(即我的假设是否正确)。
    【解决方案2】:

    将有效的 YAML 文件作为输出的更好选择可能是使用 PyYAML,这样您就可以读取所有 YAML 文件,将它们合并到内存中,然后将生成的对象转储到新文件中。

    【讨论】:

      猜你喜欢
      • 2020-11-08
      • 1970-01-01
      • 1970-01-01
      • 2015-03-28
      • 1970-01-01
      • 2016-09-05
      • 2021-09-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多