Python PyYML在ssh公钥中插入换行符
我正在做一个项目,从MySQL数据库中提取用户信息,并将其格式化为yaml文件,Ansible可以读取该文件并将其用作vars文件。我需要所有正常的用户信息、用户名、电子邮件等,以及数据库中的公共ssh密钥 问题是,PyYAML在pubkey的电子邮件部分之前插入了一个额外的换行符,我不知道为什么。下面是一个简单的例子:Python PyYML在ssh公钥中插入换行符,python,yaml,Python,Yaml,我正在做一个项目,从MySQL数据库中提取用户信息,并将其格式化为yaml文件,Ansible可以读取该文件并将其用作vars文件。我需要所有正常的用户信息、用户名、电子邮件等,以及数据库中的公共ssh密钥 问题是,PyYAML在pubkey的电子邮件部分之前插入了一个额外的换行符,我不知道为什么。下面是一个简单的例子: import yaml yamldict = { "users": [] } yamldict["users"].append({ "username": "use
import yaml
yamldict = { "users": [] }
yamldict["users"].append({
"username": "user",
"name": "user",
"sshkey": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDHV/xbvOHuPq6WbBhtmjUWKYPrqQlkILf8b/I6V9dZVBPzmhRZFCAf/gWny0hmZ95bVRED4iCSTCtN3Lq2VZiZ/kwBO7Y9E4vr1wVQYrr4IIwEhdaifZmWFLlwOXbt76dxJQs2xS9Z5ZQjEzZBFZqgYu42QbSi7tKBNSaLadOWbB3sq0IOzCZeSgrELlZIuUy7u1RbcS4w2Y29S3XLrbi2yVdVbPW8B9PfsG1n4q2/XR7w3gqhP6c8ibO4jYpADLZuHZvuoVpjKINO4kSdrwUfD8rl3MBIAD/Nu9sy0bIiKdSONQohxcsjMevxPOijjz4EiI1Ad4U6dDJrFlT0asYH user@email.com"
})
哪些产出:
users:
- name: user
sshkey: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDHV/xbvOHuPq6WbBhtmjUWKYPrqQlkILf8b/I6V9dZVBPzmhRZFCAf/gWny0hmZ95bVRED4iCSTCtN3Lq2VZiZ/kwBO7Y9E4vr1wVQYrr4IIwEhdaifZmWFLlwOXbt76dxJQs2xS9Z5ZQjEzZBFZqgYu42QbSi7tKBNSaLadOWbB3sq0IOzCZeSgrELlZIuUy7u1RbcS4w2Y29S3XLrbi2yVdVbPW8B9PfsG1n4q2/XR7w3gqhP6c8ibO4jYpADLZuHZvuoVpjKINO4kSdrwUfD8rl3MBIAD/Nu9sy0bIiKdSONQohxcsjMevxPOijjz4EiI1Ad4U6dDJrFlT0asYH
user@email.com
username: user
我尝试了许多不同的方法去除多余的空格、换行符和回车符。我还尝试将这个dict转换为json,ssh密钥看起来很好,然后在json上运行yaml.dump,它仍然给了我额外的换行符
知道我做错了什么吗?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规范,特别是上的部分
现在,由于ssh密钥中没有足够的空间,这种特殊的折叠并不能真正提高可读性。因此,您不妨增加行宽度,将所有内容放在一行上。您可以使用PyYAML实现这一点,但我建议您使用PyYAML,它支持较新的YAML 1.2标准,允许映射和序列使用单独的缩进值,并且修复了许多PyYAML问题(免责声明:我是该软件包的作者): 此转储为:
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读取数据后,在某处将键定义为preserved scalarstring。例如,在您的示例中,您可以:
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))
这将输出:
users:
- name: user
sshkey: |-
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDHV/xbvOHuPq6WbBhtmjUWKYPrqQlkIL
f8b/I6V9dZVBPzmhRZFCAf/gWny0hmZ95bVRED4iCSTCtN3Lq2VZiZ/kwBO7Y9E4vr1wVQ
Yrr4IIwEhdaifZmWFLlwOXbt76dxJQs2xS9Z5ZQjEzZBFZqgYu42QbSi7tKBNSaLadOWbB
3sq0IOzCZeSgrELlZIuUy7u1RbcS4w2Y29S3XLrbi2yVdVbPW8B9PfsG1n4q2/XR7w3gqh
P6c8ibO4jYpADLZuHZvuoVpjKINO4kSdrwUfD8rl3MBIAD/Nu9sy0bIiKdSONQohxcsjMe
vxPOijjz4EiI1Ad4U6dDJrFlT0asYH user@email.com
username: user
这真的重要吗?通常,键后面的部分是纯描述性的,对算法本身没有意义。我不确定,是吗?这将最终用于创建ssh帐户,我不确定额外的换行是否会导致ansible的授权密钥模块出现问题……我想我应该测试一下,也许不再痴迷于此……感谢全面的回答,这太棒了。不幸的是,我现在需要继续使用PyYAML,因为我运行的是旧的Centos6服务器,不想到处安装新东西。但我肯定会在我有更多控制权的其他地方开始使用你的库。@Jasonewitt通常情况下,库的选择受到环境的限制,这就是为什么我在PyYAML中包括了如何做这些。我总是使用VirtualNVS(与系统Python或更新版本一起)作为我制作的实用程序/程序,以避免弄乱系统Python的安装。顺便说一句,有一个-1分的公认答案很有趣,我最近一定踩到了别人的脚。。。
users:
- name: user
sshkey: |-
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDHV/xbvOHuPq6WbBhtmjUWKYPrqQlkIL
f8b/I6V9dZVBPzmhRZFCAf/gWny0hmZ95bVRED4iCSTCtN3Lq2VZiZ/kwBO7Y9E4vr1wVQ
Yrr4IIwEhdaifZmWFLlwOXbt76dxJQs2xS9Z5ZQjEzZBFZqgYu42QbSi7tKBNSaLadOWbB
3sq0IOzCZeSgrELlZIuUy7u1RbcS4w2Y29S3XLrbi2yVdVbPW8B9PfsG1n4q2/XR7w3gqh
P6c8ibO4jYpADLZuHZvuoVpjKINO4kSdrwUfD8rl3MBIAD/Nu9sy0bIiKdSONQohxcsjMe
vxPOijjz4EiI1Ad4U6dDJrFlT0asYH user@email.com
username: user