Мне нужно просмотреть словарь и добавить записи в объектный файл json option.json. у меня есть код
for K in "${!MYMAP[@]}"; do
opt = "{
\"OptionName\":\"${K}\",
\"Value\":\"${MYMAP[$K]}\"
},"
echo $opt >> option.json
done
Но это создает option.json, как показано ниже.
{
"OptionName": "name",
"Value": "test"
},
{
"OptionName": "age",
"Value": "13"
}
Как мне сделать option.json объектом json и добавить каждый {} внутрь объекта, чтобы мой option.json выглядел так, как показано ниже
[
{
"OptionName": "name",
"Value": "test"
},
{
"OptionName": "age",
"Value": "13"
}
]
Если вы опустите конечную запятую при первоначальном выводе, вы получите поток входных данных, который jq может собрать в массив с помощью флага --slurp
(или -s
):
for k in "${!mymap[@]}"; do
opt = "{\"OptionName\":\"${k}\", \"Value\":\"${mymap[$k]}\"}"
echo "$opt" # no comma here --^
done | jq -s . > option.json
Однако вы также можете заставить jq сначала выполнять композицию JSON, гарантируя корректную кодировку JSON даже со сложными значениями (такими как двойные кавычки и т. д.). Например, я бы использовал опцию --args
для импорта ключей и значений массива bash в качестве позиционных параметров, а встроенную функцию $ARGS
для доступа к ним:
declare -A mymap=([name] = "test" [age] = "13")
jq -n '
$ARGS.positional | [_nwise(length/2)] | transpose
| map({OptionName: first, Value: last})
' --args "${!mymap[@]}" "${mymap[@]}" > option.json
Как отметил @oguz, новые версии bash также могут расширять массивы до их ключей и значений в желаемом порядке чередования, используя "${mymap[@]@k}"
. Полный фильтр jq тогда будет просто читать:
jq -n '$ARGS.positional | [_nwise(2) | {OptionName: first, Value: last}]' \
--args "${mymap[@]@k}"
Новые версии поддерживают bash "${mymap[@]@k}"
, что устраняет необходимость транспонирования.
@oguz Это хорошее улучшение, спасибо.
Эта функция, очевидно, была добавлена в bash 5.1 (см. unix.stackexchange.com/questions/366581/…).
@peak Согласно этому журналу изменений Bash, @K
(все пары, цитируемые одним словом) были добавлены в версии 5.1 (см. 5.1.hh), но @k
(каждый элемент как отдельные слова, который используется здесь) был добавлен в версии 5.2 ( см. 5.2.т)
Теперь @pmf, если я хочу добавить третий атрибут с опцией ниже: как мне выполнить часть сопоставления, я новичок в jq, поэтому не знаю, как сделать `map({OptionName: first, Value: Last})` для `map({NewAttribute: first, OptionName: Second???, Value: Last})` NewAttribute не является частью jq -n ' $ARGS.positional | [_nwise(length/2)] | transpose | map({OptionName: first, Value: last}) ' --args "${!mymap[@]}" "${mymap[@]}" > option.json
, а является просто статическим значением
я пытался использовать nth(1), как показано ниже, объявляйте -A mymap=([name] = "test" [age] = "13") jq -n ' $ARGS.positional | [_nwise(length/3)] | transpose | map({NewAttributes: first, OptionName: nth(1), Value: last}) ' --args "test" "${!MYMAP[@]}" "${MYMAP[@]}" > option.json
но результат во втором элементе неправильный ` [ { "NewAttributes": "test", "OptionName": "name", "Value": "test" }, { "NewAttributes": "age", "OptionName": "13", "Value": null } ] `
кажется, я просто жестко запрограммировал новые атрибуты на карте map({"NewAttributes": "NewValue", OptionName: first, Value: last})
@tanfei12345 Чтобы получить статическое значение, попробуйте jq -n '… {NewAttributes: "NewValue", OptionName: first, Value: last} …' --args …
. Чтобы импортировать его из оболочки, попробуйте jq -n '… {$NewAttributes, OptionName: first, Value: last} …' --arg NewAttributes "NewValue" --args …
. Также ознакомьтесь с руководством . Опции --arg
и --args
описаны в разделе Вызов jq.
Конечно, лучше использовать jq
, если вы работаете с json, но в случае, когда jq
недоступен, printf
можно использовать, например:
declare -A arr=([key1]=val1 [key2]=val2)
items=$(
for key in "${!arr[@]}"; {
val=${arr[$key]}
printf '{"%s": "%s"},\n' "$key" "$val"
}
)
echo "[${items%,*}]"
[{"key2": "val2"},
{"key1": "val1"}]
Вот я использую jq
для проверки результата:
$ echo "[${items%,*}]" | jq
[
{
"key2": "val2"
},
{
"key1": "val1"
}
]
Обратите внимание на этот синтаксис ${items%,*}
, он будет печатать $items
без последней запятой, что очень важно для json.
Но да, некоторые хитрые значения, такие как значения из комментариев, могут все испортить, и их необходимо обойти:
declare -A arr=([key1]=val1 [key2]=val2 [key3]='foo "bar \ bar"')
items=$(
for key in ${!arr[@]}; {
printf -vval -- '%q' "${arr[$key]}"
printf -vkey -- '%q' "$key"
printf '{"%s": "%s"},\n' "${key//'\ '/ }" "${val//'\ '/ }"
}
)
echo "[${items%,*}]" | jq
[
{
"key2": "val2"
},
{
"key3": "foo \"bar \\ bar\""
},
{
"key1": "val1"
}
]
Пока вы не получите значение типа arr[key3]='foo "bar \ bar"'
, у вас неверный JSON.
Повторная реализация решения Ивана, сохраняющая ваше форматирование -
$: mymap=( [name]=test [age]=13 )
$: fmt='%s {\n "OptionName": "%s",\n "Value": "%s"\n }'
$: for k in ${!mymap[@]}; do printf -v list "$fmt" "${list:+$list,$'\n'}" "$k" "${mymap[$k]}"; done
$: printf '[\n%s\n]\n' "$list"
[
{
"OptionName": "age",
"Value": "13"
},
{
"OptionName": "name",
"Value": "test"
}
]
Но я снова указываю на комментарий Гленна: нечетные данные легко сломать.
Обратите внимание, что использование конкатенации строк для создания структурированных данных подвержено ошибкам: как только в вашем значении появятся символы, которые необходимо экранировать, чтобы оно было действительным JSON, вы начнете генерировать недопустимый синтаксис в качестве вывода. Гораздо лучше использовать такой инструмент, как
jq
, который понимает синтаксис JSON и может выполнять экранирование от вашего имени.