将多个yaml文件与python中的缩进相结合

时间:2018-02-01 11:37:12

标签: python yaml ruamel.yaml

我有以下目录和文件结构。我想在以下文件夹中的所有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格式。

我尝试了什么?

我想到写入文件<folder_name>/file_name>,然后给出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个空格后开始写文件(原样)。

2 个答案:

答案 0 :(得分:1)

您不能只将文件内容转储到YAML文档(或文档)中,就像加载内容将被解析一样。这样的解析内容可能是不正确的YAML,导致加载程序错误,或者它可能是正确的YAML导致数据结构不太可能完全转换为原始文件读取的(字符串)内容。后者是因为YAML转储器规范了缩进,大多数转储器处理了行尾注释。

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

然后,如果任何路径或文件的名称中包含有效的文件名字符,那么将#(如在您的代码中)作为路径元素和文件名之间的分隔符的方法将不起作用。您应该使用保留字符(在Unix上 - 喜欢NUL字符或/),或者通过将路径分成段并将这些段加上文件名放在一系列字符串标量中来使事物更易于移植。

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

除了程序中的评论之外,还有一些注意事项:

  1. CASSANDRA文件是专门制作的二进制文件(gpg编码,文件名为文件内容all.template的多倍的密钥)。获取!!binary标记没有什么特别需要。
  2. host.template末尾有两个空行,因此YAML会自动转储|+
  3. 在阅读文件时,如果可移植性很重要,请确保通过使用/拆分然后使用os.sep进行合并来重建路径+文件名。

答案 1 :(得分:0)

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