melon@machine: ~/$ cat /tmp/test.json
[
"One Entry Here",
"Two Entry Here",
"Three Entry Here",
"Four Entry Here"
]
melon@machine: ~/$ jq -sr '.[]| @sh' /tmp/test.json
'One Entry Here' 'Two Entry Here' 'Three Entry Here' 'Four Entry Here'
melon@machine: ~/$ BANANA=( 'One Entry Here' 'Two Entry Here' 'Three Entry Here' 'Four Entry Here' )
melon@machine: ~/$ echo ${BANANA[1]}
Two Entry Here
上面显示了预期的行为,并向我展示了手动设置命令$BANANA
的输出jq
。
如果变量使用命令替换,则此行为会改变:
melon@machine: ~/$ BANANA=( $(jq -sjr '.[]| @sh' /tmp/test.json) )
melon@machine: ~/$ echo ${BANANA[1]}
melon@machine: ~/$ echo ${BANANA[0]}
'One Entry Here' 'Two Entry Here' 'Three Entry Here' 'Four Entry Here'
虽然我确实试图将 JSON 数组转换为 BASH 数组以进行迭代,并且还有其他建议的方法,但我的问题是为什么使用命令替换时这里的行为会有所不同,我可以做些什么来让命令替换的行为像我手动将输出复制并粘贴jq
到变量中时一样?
GNU bash, version 5.0.3(1)-release (x86_64-pc-linux-gnu)
GNU bash, version 5.1.4(1)-release (x86_64-pc-linux-gnu)
melon@machine: ~/$ mapfile -t BANANA < <(jq '.[]' /tmp/test.json)
melon@machine: ~/$ echo ${BANANA[0]}
"One Entry Here"
melon@machine: ~/$ echo ${BANANA[1]}
"Two Entry Here"
mapfile
有效,但我仍然好奇使用命令替换时的区别。
即使添加-j
标志来jq
抑制结束换行符,结果仍然是一样的。
答案1
我会做以下其中一件事:
IFS=$'\t' read -ra banana < <(jq -sr '.[] | @tsv' test.json)'
得到预期结果:
$ declare -p banana
declare -a banana=([0]="One Entry Here" [1]="Two Entry Here" [2]="Three Entry Here" [3]="Four Entry Here")'
或者使用declare
类似的eval
但仅用于变量分配:
declare -a "banana=($(jq -sr '.[] | @sh' test.json))"
这会强制 shell 进行第二轮扩展,以便 jq 发出的引号能够被 shell 正确处理。否则,您会得到:
$ banana=($(jq -sr '.[] | @sh' test.json))
$ declare -p banana
declare -a banana=([0]="'One" [1]="Entry" [2]="Here'" [3]="'Two" [4]="Entry" [5]="Here'" [6]="'Three" [7]="Entry" [8]="Here'" [9]="'Four" [10]="Entry" [11]="Here'")
或者
$ banana=("$(jq -sr '.[] | @sh' test.json)")
$ declare -p banana
declare -a banana=([0]="'One Entry Here' 'Two Entry Here' 'Three Entry Here' 'Four Entry Here'")