给定这个文件:
$ cat fruits.json
[
{ "name": "apple" },
{ "name": "banana\nfofanna" },
{ "name": "my kiwi" }
]
如何使用它jq
来检索 shell 用作数据的水果名称列表,例如填充数组?
相当于fruits
下面数组的赋值:
$ fruits=(
'apple'
'banana
fofanna'
'my kiwi'
)
$ for f in "${fruits[@]}" ;do echo "<$f>"; done
<apple>
<banana
fofanna>
<my kiwi>
以下均无效:
$ fruits=( $(jq -r ' .[].name ' fruits.json))
$ fruits=( $(jq -r ' .[].name | @sh ' fruits.json))
$ fruits=( $(jq ' .[].name | @sh ' fruits.json))
答案1
问题末尾的前两次尝试不起作用,因为它们依赖于 shell 分割jq
空白的输出。由于换行符是一种空白,因此您会丢失数据中的换行符(以及制表符和原始空格)。
此外,这些尝试也无法引用字符串数据,因此如果任何字符串包含文件名通配符,您就会发生文件名通配符。
最后一次尝试由于类似的原因失败,但另外也没有正确解码数据,将编码的换行符留在原处。
使用内置运算符@sh
injq
引用数据并构建数组赋值:
eval "$( jq -r '"fruits=(" + (map(.name)|@sh) + ")"' fruits.json )"
对于给定的 JSON 数据,这将导致当前 shell 评估以下赋值,从而创建数组fruits
:
fruits=('apple' 'banana
fofanna' 'my kiwi')
在评估该声明后,
$ printf '<%s>\n' "${fruits[@]}"
<apple>
<banana
fofanna>
<my kiwi>
作为替代方案,以下内容将附加name
shell 数组中每个元素的值:
$ jq -r '"fruits+=(" + (.[].name | @sh) + ")"' fruits.json
fruits+=('apple')
fruits+=('banana
fofanna')
fruits+=('my kiwi')
$ unset -v fruits
$ eval "$(jq -r '"fruits+=(" + (.[].name | @sh) + ")"' fruits.json)"
$ printf '<%s>\n' "${fruits[@]}"
<apple>
<banana
fofanna>
<my kiwi>
答案2
你没有提到什么shell;对于 ZSH,人们可能会使用
% fru=( ${(fQg::)"$(jq '.[].name' ~/tmp/fru)"} )
% for f in $fru; printf '<%s>\n' $f
<apple>
<banana
fofanna>
<my kiwi>
% =echo -n "<${fru[2]}>" | od -bc
0000000 074 142 141 156 141 156 141 012 146 157 146 141 156 156 141 076
< b a n a n a \n f o f a n n a >
0000020
它执行jq
调用,然后f
将结果拆分为换行符,并删除输出在字符串上留下的Q
引用,以及jq
g::
作用类似于内置回声来执行插值\n
在输入中的字符串上。
答案3
基于 @Kusalananda、@roaima 和 @StéphaneChazelas 的回答和评论以及上面评论中提到的类似问题,我能够想出:
list=$( jq -r ' .[].name | @sh ' fruits.json ) || exit 1
eval "fruits=( $list )" || exit 1
printf "<%s>\n" "${fruits[@]}"