我想将bash中的关联数组转换为json hash / dict。我宁愿使用jq来做这个,因为它已经是一个依赖项,我可以依靠它来生成格式良好的json。有人可以演示如何实现这个目标吗?
#!/bin/bash
declare -A dict=()
dict["foo"]=1
dict["bar"]=2
dict["baz"]=3
for i in "${!dict[@]}"
do
echo "key : $i"
echo "value: ${dict[$i]}"
done
echo 'desired output using jq: { "foo": 1, "bar": 2, "baz": 3 }'
答案 0 :(得分:5)
有很多可能性,但鉴于您已经编写了一个bash for
循环,您可能希望从脚本的这种变体开始:
#!/bin/bash
# Requires bash with associative arrays
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作为" line-end"字符,如下所示。
为了使上述更通用,最好将键和值呈现为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常见问题解答中提到并由@CharlesDuffy提倡。它在将bash键和值传递给jq时使用NUL作为分隔符,并且具有仅需要一次调用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
}
}
答案 1 :(得分:4)
此答案来自nico103
freenode
上的#jq
:
#!/bin/bash
declare -A dict=()
dict["foo"]=1
dict["bar"]=2
dict["baz"]=3
assoc2json() {
declare -n v=$1
printf '%s\0' "${!v[@]}" "${v[@]}" |
jq -Rs 'split("\u0000") | . as $v | (length / 2) as $n | reduce range($n) as $idx ({}; .[$v[$idx]]=$v[$idx+$n])'
}
assoc2json dict
答案 2 :(得分:1)
已发布,并在IRC上记入nico103,也就是说,我。
让我害怕的事情当然是这些关联数组键和值需要引用。这是一个开始,需要一些额外的工作来取消键和值:
function assoc2json {
typeset -n v=$1
printf '%q\n' "${!v[@]}" "${v[@]}" |
jq -Rcn '[inputs] |
. as $v |
(length / 2) as $n |
reduce range($n) as $idx ({}; .[$v[$idx]]=$v[$idx+$n])'
}
$ assoc2json a
{"foo\\ bar":"1","b":"bar\\ baz\\\"\\{\\}\\[\\]","c":"$'a\\nb'","d":"1"}
$
所以现在所需要的只是一个jq函数来删除引号,它有多种形式:
我把这最后一个留给读者作为练习。
我应该注意,我在这里使用printf
作为迭代器!
答案 3 :(得分:0)
您可以将变量初始化为空对象{}
,并为每次迭代添加键/值{($key):$value}
,并将结果重新注入相同的变量中:
#!/bin/bash
declare -A dict=()
dict["foo"]=1
dict["bar"]=2
dict["baz"]=3
data='{}'
for i in "${!dict[@]}"
do
data=$(jq -n --arg data "$data" \
--arg key "$i" \
--arg value "${dict[$i]}" \
'$data | fromjson + { ($key) : ($value | tonumber) }')
done
echo "$data"