Я читаю данные из файла json с помощью jq. Я хочу добавить результаты в файл yaml, но у меня ничего не получается. Я новичок в программировании оболочки. Моя цель - добавить эти «пользователи» к существующему массиву «пользователей» в файле 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 [email protected]"]
}
]
}
Я фильтрую это с помощью этого:
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.
Любая помощь или подсказка были бы хороши, заранее спасибо ...
Это прямо противоположное направление
Сам Bash не подходит для этого. Найдите существующий инструмент или напишите его, например, Python. В зависимости от вашего предпочтительного языка не составит труда найти существующий вопрос о переполнении стека с предложениями; вот поиск Python
Пожалуйста, следуйте правилам минимальный воспроизводимый пример. В частности, будет полезен образец my.yml (т.е. input).
yq r input.json должен сделать эту работу.
Спасибо всем, я последовал подходу @Inian, чтобы написать lil-скрипт на Ruby. и @ Джефф Меркадо. Не знаю, почему это цитируется, но это именно то, на что программа наплевывает.
как отметил @AHT, yq r работает (поскольку JSON является подмножеством YAML); вывод должен быть красиво напечатан, хотя чтобы он «больше походил на YAML»: yq -P r input.json; см. также mikefarah.gitbook.io/yq/usage/convert#json-to-yaml

Я не уверен, какие правила вы используете, чтобы получить ожидаемый результат. Похоже, вы случайным образом применяете разные правила к тому, как конвертируются значения.
Насколько я понимаю, скалярные значения просто выводятся как есть (с потенциальной кодировкой), объекты выводятся как пары ключ / значение, а объекты массивов выводятся с - для каждого элемента. Отступ ассоциирует то, что является частью чего.
Итак, исходя из этих правил, если вы собираетесь использовать 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 [email protected]
Вот еще один вариант, который выравнивает значения
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 [email protected]
очень круто. люблю, к сожалению, он ломается для многострочных строковых значений ..
На основе кода @Jeff Mercado я добавил код для поддержки многострочных строк и экранирования одинарных кавычек. См. Мой ответ «Решение в jq (без других инструментов)».
Я использовал Ruby, чтобы записать свой json-контент в yaml.
Что касается вашего примера, это можно сделать так:
cat $DEFAULTS_FILE | jq .users | ruby -ryaml -rjson -e 'puts YAML.dump(JSON.parse(STDIN.read))' > my.yml
Еще один лайнер:
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, которое должно работать аналогичным образом. Однострочник Ричарда Гомеса отлично работает.
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())))'
}
Больше трюков Bash на http://github.com/frgomes/bash-scripts
Возможно, излишний в свете yq и jq, но сделан очень хорошо и, вероятно, имеет хорошие приложения.
Ха! Так случилось, что я столкнулся с ошибками при установке yq только сейчас в ноутбуке gpc ai. так что, я думаю, они вам очень пригодятся!
возможно, лучший ответ, так как он не заставляет вас устанавливать yq
yq обертка yaml для jq
В yq версии 4.8.0:
cat $DEFAULTS_FILE | yq e -P -
e или eval обрабатывают файл отдельно. ea или eval-all сначала объединят файлы.-P или --prettyPrint YAML выход- от STDINПримечание: вы также можете пойти другим путем (от yaml к json) yq e -j file.yaml
В yq версии 3.3.2:
cat $DEFAULTS_FILE | yq r -P -
r читать-P --prettyPrint- от STDINТы сумасшедший парень! Спасибо друг!
Этот ответ требует редактирования. Ваш пример yq - это реализация mikefarah / yq, которая НЕ является оболочкой для jq. Смотрите мой отвечать
Если вы используете 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.15 и yq e -P, и yq eval -P работают нормально.
Я предлагаю использовать 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 [email protected]
yq eval -Pс mikefarah/yq версии 4.0 (выпущен в декабре 2020 г.), устанавливается через большинство менеджеров пакетов Unix-подобных ОС: через Homebrew для macOS (brew install yq), Debian с apt (apt install yq), Alpine с apk (apk add yq) и т. д.
См. Работа с JSON.
To read in json, just pass in a json file instead of yaml, it will just work - as json is a subset of yaml. However, you will probably want to use the Style Operator or
--prettyPrint/-Pflag to make look more like an idiomatic yaml document.
На основе кода @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"
}
}
Вы можете проверить правильность функции toYaml, преобразовав json в yaml, а затем обратно в json с помощью yq.
FILE='containsMultilineStrings.json'; diff <(cat "$FILE") <(jq -r 'toYaml' $FILE | yq)
Быстрый тест показывает сокращение времени работы функции toYaml по сравнению с использованием yq.
На своем компьютере я измерил:
time for i in {1..100}; do yq -y > /dev/null < containsMultilineStrings.json; done
8.4 sec
time for i in {1..100}; do jq -r 'toYaml' > /dev/null containsMultilineStrings.json; done
3.4 sec
Вы можете использовать
python/perlсо встроенными модулямиYAMLиJSON- commandlinefu.com/commands/view/12218/convert-yaml-to-json