如何在 bash 中将 json 响应转换为 yaml
Posted
技术标签:
【中文标题】如何在 bash 中将 json 响应转换为 yaml【英文标题】:How to convert a json response into yaml in bash 【发布时间】:2019-04-18 08:09:02 【问题描述】:我使用 jq 从 json 文件中读取数据。我想将结果附加到一个 yaml 文件中,但我没有让它工作。我对 shell 编程很陌生。我的目标是将“用户”附加到 yaml 文件中现有的“用户”数组中。
这是我的 json 文件:
#$DEFAULTS_FILE
"users":
[
"name":"pi",
"gecos": "Hypriot Pirate",
"sudo":"ALL=(ALL) NOPASSWD:ALL",
"shell": "/bin/bash",
"groups":"users,docker,video",
"plain_text_passwd":"pi",
"lock_passwd":"false",
"ssh_pwauth":"true",
"chpasswd": "expire": false
,
"name":"admin",
"gecos": "Hypriot Pirate",
"sudo":"ALL=(ALL) NOPASSWD:ALL",
"shell": "/bin/bash",
"primary-group": "users",
"groups":"users,docker,adm,dialout,audio,plugdev,netdev,video",
"ssh-import-id":"None",
"plain_text_passwd":"pi",
"lock_passwd":"true",
"ssh_pwauth":"true",
"chpasswd": "expire: false",
"ssh-authorized-keys": ["ssh-rsa abcdefg1234567890 YOUR_KEY@YOURHOST.local"]
]
我用那个过滤它:
cat $DEFAULTS_FILE | jq .users
我不知道如何将该 json 转换为 yaml。
我的预期结果应该是:
users:
- name: pi
gecos: "Hypriot Pirate"
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
groups: users,docker,video
plain_text_passwd: pi
lock_passwd: false
ssh_pwauth: true
chpasswd: expire: false
- name: admin
primary-group: users
shell: /bin/bash
sudo: ALL=(ALL) NOPASSWD:ALL
groups: users,docker,adm,dialout,audio,plugdev,netdev,video
ssh-import-id: None
我尝试使用名为yq
的第二个工具,它类似于jq
,可以编写yaml 文件。但我没有积极的进展。
编辑
我知道我可以将内容添加到 yaml 中:
yq w -i "my.yml" "users[+]" "some content"
但我不知道如何将我的 json 合并到其中。
任何帮助或提示都会很好,在此先感谢您...
【问题讨论】:
你可以使用python
/perl
内置YAML
和JSON
模块 - commandlinefu.com/commands/view/12218/convert-yaml-to-json
正好相反
Bash 本身并不是一个很好的平台。找到一个现有的工具,或者写一个,例如Python。根据您的首选语言,在 Stack Overflow 上找到现有问题并提供建议应该不难; here's a search for Python
请遵循minimal reproducible example 准则。特别是,一个示例 my.yml(即输入)会很有帮助。
yq r input.json
应该可以完成这项工作。
【参考方案1】:
yq
jq
的 yaml 包装器
使用 yq 版本 4.8.0:
cat $DEFAULTS_FILE | yq e -P -
e
或 eval
分别处理文件。 ea
或 eval-all
将首先合并文件。
-P
或 --prettyPrint
YAML 输出
-
来自标准输入
注意:你也可以采用其他方式(yaml 到 json)yq e -j file.yaml
使用 yq 版本 3.3.2:
cat $DEFAULTS_FILE | yq r -P -
r
阅读
-P
--prettyPrint
-
来自标准输入
【讨论】:
你是个疯子!谢谢老哥! 这个答案需要修改。您的yq
示例是mikefarah/yq 的实现,它不是jq
的包装器。见我的answer
如果使用mikefarah/yq V4,需要使用如下命令:yq eval '.. style= ""' sample.json
或cat sample.json | yq eval '.. style= ""' -
。见mikefarah.gitbook.io/yq/usage/convert
其实你只需要cat $DEFAULTS_FILE | yq -y
对于mikefarah/yq
版本4,yq eval -P
是正确的语法。【参考方案2】:
function yaml_validate
python -c 'import sys, yaml, json; yaml.safe_load(sys.stdin.read())'
function yaml2json
python -c 'import sys, yaml, json; print(json.dumps(yaml.safe_load(sys.stdin.read())))'
function yaml2json_pretty
python -c 'import sys, yaml, json; print(json.dumps(yaml.safe_load(sys.stdin.read()), indent=2, sort_keys=False))'
function json_validate
python -c 'import sys, yaml, json; json.loads(sys.stdin.read())'
function json2yaml
python -c 'import sys, yaml, json; print(yaml.dump(json.loads(sys.stdin.read())))'
http://github.com/frgomes/bash-scripts 的更多 Bash 技巧
【讨论】:
鉴于yq
和jq
可能是多余的,但做得非常好,并且可能有一些很好的应用程序。
哈!碰巧的是,我刚刚在 gpc ai 笔记本中安装yq
时遇到了错误。所以,我想这些会派上用场的!
也许是最好的答案,因为它不会让你安装yq
很棒的 bash 脚本。
yq 太臃肿了。谢谢。【参考方案3】:
我不确定您使用什么规则来获得预期结果。您似乎在随机应用不同的规则来转换值的方式。
据我了解,标量值只是按原样输出(使用潜在编码),对象作为键/值对输出,数组对象输出每个项目都带有 -
。缩进关联什么是什么的一部分。
因此,如果您要使用 jq,请根据这些规则:
def yamlify:
(objects | to_entries[] | (.value | type) as $type |
if $type == "array" then
"\(.key):", (.value | yamlify)
elif $type == "object" then
"\(.key):", " \(.value | yamlify)"
else
"\(.key):\t\(.value)"
end
)
// (arrays | select(length > 0)[] | [yamlify] |
" - \(.[0])", " \(.[1:][])"
)
// .
;
然后要使用它,请将其添加到您的 .jq
文件并使用它:
$ jq -r yamlify input.json
users:
- name: pi
gecos: Hypriot Pirate
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
groups: users,docker,video
plain_text_passwd: pi
lock_passwd: false
ssh_pwauth: true
chpasswd:
expire: false
- name: admin
gecos: Hypriot Pirate
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
primary-group: users
groups: users,docker,adm,dialout,audio,plugdev,netdev,video
ssh-import-id: None
plain_text_passwd: pi
lock_passwd: true
ssh_pwauth: true
chpasswd: expire: false
ssh-authorized-keys:
- ssh-rsa abcdefg1234567890 YOUR_KEY@YOURHOST.local
这是对齐值的另一个变体
def yamlify2:
(objects | to_entries | (map(.key | length) | max + 2) as $w |
.[] | (.value | type) as $type |
if $type == "array" then
"\(.key):", (.value | yamlify2)
elif $type == "object" then
"\(.key):", " \(.value | yamlify2)"
else
"\(.key):\(" " * (.key | $w - length))\(.value)"
end
)
// (arrays | select(length > 0)[] | [yamlify2] |
" - \(.[0])", " \(.[1:][])"
)
// .
;
$ jq -r yamlify2 input.json
users:
- name: pi
gecos: Hypriot Pirate
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
groups: users,docker,video
plain_text_passwd: pi
lock_passwd: false
ssh_pwauth: true
chpasswd:
expire: false
- name: admin
gecos: Hypriot Pirate
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
primary-group: users
groups: users,docker,adm,dialout,audio,plugdev,netdev,video
ssh-import-id: None
plain_text_passwd: pi
lock_passwd: true
ssh_pwauth: true
chpasswd: expire: false
ssh-authorized-keys:
- ssh-rsa abcdefg1234567890 YOUR_KEY@YOURHOST.local
【讨论】:
真的很酷。喜欢它,不幸的是,它会因多行字符串值而中断.. 基于@Jeff Mercado 的代码,我添加了一些代码来支持多行字符串和单引号的转义。请参阅我的回答“jq 中的解决方案(没有其他工具)”。【参考方案4】:yq eval -P
带有mikefarah/yq
4.0 版(2020 年 12 月发布),可通过大多数类 Unix 操作系统包管理器安装:通过 Homebrew for macOS (brew install yq
)、Debian 和 apt
(apt install yq
)、Alpine 和 @987654328 @(apk add yq
)等
见Working with JSON。
要读取 json,只需传入一个 json 文件而不是 yaml,它就可以工作 - 因为 json 是 yaml 的一个子集。但是,您可能希望使用 Style Operator 或
--prettyPrint/-P
标志来使看起来更像一个惯用的 yaml 文档。
【讨论】:
【参考方案5】:我使用 ruby 将我的 json 内容写入 yaml。
至于你的例子,可以这样实现:
cat $DEFAULTS_FILE | jq .users | ruby -ryaml -rjson -e 'puts YAML.dump(JSON.parse(STDIN.read))' > my.yml
【讨论】:
虽然我无法忍受 ruby,但我的机器已经安装了它。我能够创建一个别名并将其传递给该别名。谢谢,这很有帮助!【参考方案6】:我建议使用yq
和-y
选项
$ pip3 install yq # requires jq
$ cat in.json | yq -y
users:
- name: pi
gecos: Hypriot Pirate
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
groups: users,docker,video
plain_text_passwd: pi
lock_passwd: 'false'
ssh_pwauth: 'true'
chpasswd:
expire: false
- name: admin
gecos: Hypriot Pirate
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
primary-group: users
groups: users,docker,adm,dialout,audio,plugdev,netdev,video
ssh-import-id: None
plain_text_passwd: pi
lock_passwd: 'true'
ssh_pwauth: 'true'
chpasswd: 'expire: false'
ssh-authorized-keys:
- ssh-rsa abcdefg1234567890 YOUR_KEY@YOURHOST.local
【讨论】:
【参考方案7】:另一个单线:
python -c 'import yaml, sys; print(yaml.dump(yaml.load(open(sys.argv[1])), default_flow_style=False))' input.json
(利用有效的 json 也是有效的 yaml 的事实)
还有 yaml 到 json:
python -c 'import yaml, json, sys; print(json.dumps(yaml.load(open(sys.argv[1])), indent=2))' input.yaml
【讨论】:
您的 yaml-to-json oneliner 失败并显示found character '\t' that cannot start any token
;这是相同的错误消息yq
报告,必须类似地工作。理查德戈麦斯的单线工作正常。【参考方案8】:
jq 中的解决方案(无需其他工具)
基于本文中@Jeff Mercado 的代码,我添加了对多行字符串和单引号转义的支持。
# purpose: converts Json to Yaml
# remarks:
# You can use 'yq -y' to convert json to yaml, but ...
# * this function can be used several times within a single jq program
# * this function may be faster than using yq
# * maybe yq is not available in your environment
#
# input: any Json
# output: json converted to yaml
def toYaml:
def handleMultilineString($level):
reduce ([match("\n+"; "g")] # find groups of '\n'
| sort_by(-.offset))[] as $match
(.; .[0:$match.offset + $match.length] +
"\n\(" " * $level)" + # add one extra '\n' for every group of '\n's. Add indention for each new line
.[$match.offset + $match.length:]);
def toYamlString($level):
if type == "string"
then handleMultilineString($level)
| sub("'"; "''"; "g") # escape single quotes
| "'\(.)'" # wrap in single quotes
else .
end;
def _toYaml($level):
(objects | to_entries[] |
if (.value | type) == "array" then
"\(.key):", (.value | _toYaml($level))
elif (.value | type) == "object" then
"\(.key):", "\(" ")\(.value | _toYaml($level))"
else
"\(.key): \(.value | toYamlString($level))"
end
)
// (arrays | select(length > 0)[] | [_toYaml($level)] |
" - \(.[0])", "\(" ")\(.[1:][])"
)
// .;
_toYaml(1);
示例用法
File 'containsMultilineStrings.json'
"response":
"code": 200,
"message": "greeting\nthat's all folks\n\n\n"
jq -r 'toYaml' < containsMultilineStrings.json
response:
code: 200
message: 'greeting
that''s all folks
'
jq -r 'toYaml' containsMultilineStrings.json | yq
(往返)
"response":
"code": 200,
"message": "greeting\nthat's all folks\n\n\n"
测试
您可以通过将 json 转换为 yaml 并使用 yq 转换回 json 来测试函数 toYaml
的正确性。
FILE='containsMultilineStrings.json'; diff <(cat "$FILE") <(jq -r 'toYaml' $FILE | yq)
性能
与使用 yq 相比,快速基准测试显示函数 toYaml
的运行时间减少。
在我的电脑上,我测量了:
time for i in 1..100; do yq -y > /dev/null < containsMultilineStrings.json; done
8.4 秒
time for i in 1..100; do jq -r 'toYaml' > /dev/null containsMultilineStrings.json; done
3.4 秒
【讨论】:
【参考方案9】:另一种选择是使用gojq。它是 jq 的一个端口,支持读写 yaml。它可以通过 GitHub 版本、自制软件和零安装来安装。您的问题的命令是:
cat test.json | gojq --yaml-output > test.yaml
【讨论】:
以上是关于如何在 bash 中将 json 响应转换为 yaml的主要内容,如果未能解决你的问题,请参考以下文章