使用命令替换时 BASH 数组行为不同

使用命令替换时 BASH 数组行为不同
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'")

相关内容