有许多可能性,但考虑到您已经编写了一个bash for
循环,您可能会喜欢从您的脚本的这个变体开始:
#!/bin/bash
declare -A dict
dict["foo"]=1
dict["bar"]=2
dict["baz"]=3
for i in "${!dict[@]}"
do
echo "$i"
echo "${dict[$i]}"
done |
jq -n -R 'reduce inputs as $i ({}; . + { ($i): (input|(tonumber? // .)) })'
此结果反映了bash for
循环产生的键的排序:
{
"bar": 2,
"baz": 3,
"foo": 1
}
通常,基于将键值对逐行输入jq的方法具有很多优点,每个键在一行上,随后是相应的值。以下是一个遵循此通用方案,但使用NUL作为“行尾”字符的解决方案。
将键和值呈现为JSON实体
为了使上述更加通用,最好将键和值表示为JSON实体。在这种情况下,我们可以编写:
for i in "${!dict[@]}"
do
echo "\"$i\""
echo "${dict[$i]}"
done |
jq -n 'reduce inputs as $i ({}; . + { ($i): input })'
其他变化
JSON键必须是JSON字符串,因此可能需要一些工作来确保实现所需的从bash键到JSON键的映射。类似的评论适用于从bash数组值到JSON值的映射。处理任意bash键的一种方法是让jq进行转换:
printf "%s" "$i" | jq -Rs .
当然,您也可以使用bash数组值做同样的事情,并让jq检查值是否可以转换为数字或其他所需的JSON类型(例如使用fromjson? // .
)。
一种通用解决方案
这是一个通用解决方案,沿用了jq FAQ中提到的方法,并得到@CharlesDuffy的支持。它使用NUL作为分隔符将bash键和值传递给jq,并且只需要调用一次jq即可。如果需要,过滤器fromjson? // .
可以省略或替换为另一个过滤器。
declare -A dict=( [$'foo\naha']=$'a\nb' [bar]=2 [baz]=$'{"x":0}' )
for key in "${!dict[@]}"; do
printf '%s\0%s\0' "$key" "${dict[$key]}"
done |
jq -Rs '
split("\u0000")
| . as $a
| reduce range(0; length/2) as $i
({}; . + {($a[2*$i]): ($a[2*$i + 1]|fromjson? // .)})'
输出:
{
"foo\naha": "a\nb",
"bar": 2,
"baz": {
"x": 0
}
}