PyYML在ssh公钥中插入linebreak

2024-06-16 19:06:53 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在做一个项目,从MySQL数据库中提取用户信息,并将其格式化为yaml文件,Ansible可以将其作为vars文件读取和使用。我需要所有的正常用户信息,用户名,电子邮件等,连同他们的公共ssh密钥从数据库。在

问题是,PyYAML在pubkey的email部分之前插入了一个额外的换行符,我不知道为什么。下面是一个简单的例子:

import yaml

yamldict = { "users": [] }

yamldict["users"].append({
    "username": "user",
    "name": "user",
    "sshkey": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDHV/xbvOHuPq6WbBhtmjUWKYPrqQlkILf8b/I6V9dZVBPzmhRZFCAf/gWny0hmZ95bVRED4iCSTCtN3Lq2VZiZ/kwBO7Y9E4vr1wVQYrr4IIwEhdaifZmWFLlwOXbt76dxJQs2xS9Z5ZQjEzZBFZqgYu42QbSi7tKBNSaLadOWbB3sq0IOzCZeSgrELlZIuUy7u1RbcS4w2Y29S3XLrbi2yVdVbPW8B9PfsG1n4q2/XR7w3gqhP6c8ibO4jYpADLZuHZvuoVpjKINO4kSdrwUfD8rl3MBIAD/Nu9sy0bIiKdSONQohxcsjMevxPOijjz4EiI1Ad4U6dDJrFlT0asYH user@email.com"
})

哪些输出:

^{pr2}$

我尝试了很多不同的方法来去掉多余的空格、换行符和回车符。我还尝试将这个dict转换为json,ssh密钥在那里看起来不错,然后运行山药垃圾场在json上,它仍然给了我额外的换行符。在

你知道我做错了什么吗?在


Tags: 文件项目用户信息数据库jsonyamlemail
2条回答

YAML可以用多种方式将字符串表示为标量:普通(不带引号)、单引号、双引号、带文本或折叠样式。键sshkey的值是纯标量。在

YAML也希望可读,而长行不是很可读。所以这里有一些规则,如何用宽标量来包装长线。您的普通标量即sshkey的值被包装。这意味着在YAML文档中有一个新行,但是在它所表示的标量字符串中没有新行,并且在读取YAML文档时,新行被“展开”。在

通过使用yamldict定义运行以下命令可以看到这一点:

with open('tmp.yaml', 'w') as fp:
    yaml.safe_dump(yamldict, fp)
with open('tmp.yaml') as fp:
    data = yaml.safe_load(fp)

assert '\n' in data['users'][0]['sshkey']

这将抛出一个错误,因为重新加载的ssh密钥中没有新行。在

所以你的程序很好,但是你犯的错误是你没有阅读YAML规范,特别是line folding上的部分。在


现在这种特殊的折叠并不能真正使内容更具可读性,因为ssh密钥中没有足够的空间。所以你可以增加线宽,把所有的东西都放在一条线上。您可以使用PyYAML来实现这一点,但我建议您使用^{},它支持更新的yaml1.2标准,允许映射和序列的单独缩进值,并且修复了许多PyYAML问题(免责声明:我是该包的作者):

^{pr2}$

此转储为:

users:
- username: user
  name: user
  sshkey: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDHV/xbvOHuPq6WbBhtmjUWKYPrqQlkILf8b/I6V9dZVBPzmhRZFCAf/gWny0hmZ95bVRED4iCSTCtN3Lq2VZiZ/kwBO7Y9E4vr1wVQYrr4IIwEhdaifZmWFLlwOXbt76dxJQs2xS9Z5ZQjEzZBFZqgYu42QbSi7tKBNSaLadOWbB3sq0IOzCZeSgrELlZIuUy7u1RbcS4w2Y29S3XLrbi2yVdVbPW8B9PfsG1n4q2/XR7w3gqhP6c8ibO4jYpADLZuHZvuoVpjKINO4kSdrwUfD8rl3MBIAD/Nu9sy0bIiKdSONQohxcsjMevxPOijjz4EiI1Ad4U6dDJrFlT0asYH user@email.com

您可以做的另一件事是将该键作为文本样式标量转储。为此,您需要包含一个import:from ruamel.yaml.scalarstring import PreservedScalarString,然后在从MySQL读取数据后,在某处将密钥定义为保留标量字符串。在你的例子中,你可以这样做:

for m in yamldict['users']:
    m['sshkey'] = PreservedScalarString(m['sshkey'])

假设您删除了yaml.width = 1024,并包含了yaml.indent(sequence=4, offset=2),那么这个转储将如下所示:

users:
  - username: user
    name: user
    sshkey: |-
      ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDHV/xbvOHuPq6WbBhtmjUWKYPrqQlkILf8b/I6V9dZVBPzmhRZFCAf/gWny0hmZ95bVRED4iCSTCtN3Lq2VZiZ/kwBO7Y9E4vr1wVQYrr4IIwEhdaifZmWFLlwOXbt76dxJQs2xS9Z5ZQjEzZBFZqgYu42QbSi7tKBNSaLadOWbB3sq0IOzCZeSgrELlZIuUy7u1RbcS4w2Y29S3XLrbi2yVdVbPW8B9PfsG1n4q2/XR7w3gqhP6c8ibO4jYpADLZuHZvuoVpjKINO4kSdrwUfD8rl3MBIAD/Nu9sy0bIiKdSONQohxcsjMevxPOijjz4EiI1Ad4U6dDJrFlT0asYH user@email.com

其中|-表示文本样式的块标量。在


如果您需要坚持使用PyYAML,那么使用safe_dump(yamldict, ..., width=1024),但是没有一种简单的方法可以将键作为文本样式的块标量转储,也不能只缩进序列)。在

这是我的解决方案,使用PyYAML:

import yaml

def add_line_breaks(long_string, line_len=70):
    return '\n'.join(long_string[i:i+line_len] for i in range(0, len(long_string), line_len))

def long_str_representer(dumper, data): # https://stackoverflow.com/a/33300001/10590519
    if len(data.splitlines()) > 1:  # check for multiline string
        return dumper.represent_scalar('tag:yaml.org,2002:str', data, style='|')
    return dumper.represent_scalar('tag:yaml.org,2002:str', data)

yaml.add_representer(str, long_str_representer)

yamldict = { "users": [] }

yamldict["users"].append({
    "username": "user",
    "name": "user",
    "sshkey": add_line_breaks("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDHV/xbvOHuPq6WbBhtmjUWKYPrqQlkILf8b/I6V9dZVBPzmhRZFCAf/gWny0hmZ95bVRED4iCSTCtN3Lq2VZiZ/kwBO7Y9E4vr1wVQYrr4IIwEhdaifZmWFLlwOXbt76dxJQs2xS9Z5ZQjEzZBFZqgYu42QbSi7tKBNSaLadOWbB3sq0IOzCZeSgrELlZIuUy7u1RbcS4w2Y29S3XLrbi2yVdVbPW8B9PfsG1n4q2/XR7w3gqhP6c8ibO4jYpADLZuHZvuoVpjKINO4kSdrwUfD8rl3MBIAD/Nu9sy0bIiKdSONQohxcsjMevxPOijjz4EiI1Ad4U6dDJrFlT0asYH user@email.com")
})

print(yaml.dump(yamldict, default_flow_style=False))

这将输出:

^{pr2}$

相关问题 更多 >