我用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合并到其中。
任何帮助或提示都会很好,谢谢您……
答案 0 :(得分:11)
使用yq version 3.3.2
:
cat $DEFAULTS_FILE | yq r -P -
yq
的{{1}}的Yaml包装器
jq
已读
r
--prettyPrint
-P
来自STDIN
答案 1 :(得分:8)
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技巧
答案 2 :(得分:8)
yq eval -P
带有 mikefarah/yq
4.0 版(2020 年 12 月发布),可通过大多数类 Unix 操作系统包管理器安装:通过 Homebrew for macOS (brew install yq
),Debian with apt
({{1 }})、Alpine 与 apt install yq
(apk
) 等
要读取 json,只需传入一个 json 文件而不是 yaml,它就可以工作 - 因为 json 是 yaml 的子集。但是,您可能希望使用样式运算符或 apk add yq
标志使其看起来更像惯用的 yaml 文档。
答案 3 :(得分:4)
我不确定您要使用什么规则来达到预期的结果。似乎您是随机地将不同的规则应用于如何转换值。
据我了解,标量值仅按原样输出(使用潜在编码),对象作为键/值对输出,而数组对象的每个项目都以-
输出。缩进关联什么是什么。
因此,根据这些规则,如果您要使用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
答案 4 :(得分:4)
另一个内衬:
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
答案 5 :(得分:2)
我用ruby将json内容写入yaml。
对于您的示例,可以这样实现:
cat $DEFAULTS_FILE | jq .users | ruby -ryaml -rjson -e 'puts YAML.dump(JSON.parse(STDIN.read))' > my.yml
答案 6 :(得分:0)
我建议将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 :(得分:0)
基于本文中@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 秒
答案 8 :(得分:-2)
我最终安装了宝石并使用红宝石:
gem install deep_merge
(https://github.com/danielsdeleo/deep_merge)
这是我的方法:
#!/usr/bin/env ruby
#
require 'json'
require 'yaml'
require 'deep_merge/rails_compat'
json_input_file = ARGF.argv[0]
yaml_output_file = ARGF.argv[1]
scope = ARGF.argv[2]
json = File.read(json_input_file)
yaml = File.read(yaml_output_file)
json_data = JSON.parse(json)
scoped_result = json_data[scope]
old_values_hash = YAML.load(File.read(yaml_output_file))
result = YAML.parse(yaml)
merged = old_values_hash.deeper_merge scoped_result
File.write(yaml_output_file, merged.to_yaml)
然后可以将其用于从json文件读取特定的scoped_value
。
jsonyaml.sh $JSON_FILE $YAML_TARGET "my_scope_to_add"
答案 9 :(得分:-3)
我已经可以使用以下命令获取结果:
jq -r .users defaultsfile.txt >> users.yaml
输出为清晰的Yaml格式。