2

我有以下目录和文件结构。我想从以下这些文件夹中的所有 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 个空格之后开始写入文件(原样)。

4

2 回答 2

1

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

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

然后,如果任何路径或文件的名称中包含有效的文件名字符,那么将 a #(如在您的代码中)作为路径元素和文件名之间的分隔符的方法将不起作用。您应该使用保留字符(在类 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=

除了程序中的注释外,还有几点需要注意:

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

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

于 2018-02-01T11:50:22.877 回答