【问题标题】:Assigning jq output to bash array when json value contains spaces当json值包含空格时将jq输出分配给bash数组
【发布时间】:2023-03-15 16:43:01
【问题描述】:

当我这样做时:

arr=($(echo '{"crew":[{"name":"kirk"},{"name":"bones"},{"name":"mr spock"}]}' | jq -r '.crew[].name | @sh'))

我明白了:

echo "${arr[2]}"
mr
echo "${arr[3]}"
spock

但是当我这样做时:

arr=("kirk" "bones" "mr spock")

我明白了:

echo "${arr[2]}"
mr spock

为什么在第一个示例中,bash 在创建数组时会忽略每个 jq 值所包含的引号?

【问题讨论】:

  • 你使用 jq -r 去掉了引号
  • @RamanSailopal jq 运算符 @sh 转义输出,根据 jq 手册:“输入已转义,适合在 POSIX shell 的命令行中使用。如果输入是数组,输出将是一系列以空格分隔的字符串。”
  • 你能不能... readarray -t arr
  • @RamanSailopal 是的,readarray 似乎有效。谢谢你。但是,我仍然很想知道为什么 bash 似乎删除/忽略了 jq 包装值的引号。
  • @JonHudson 见"Why does shell ignore quoting characters in arguments passed to it through variables?"(情况不同,但分词过程完全一样)。

标签: arrays json bash jq


【解决方案1】:
$ ary=($(echo '{"crew":[{"name":"kirk"},{"name":"bones"},{"name":"mr spock"}]}' | jq -r '.crew[].name | @sh'))
$ declare -p ary
declare -a ary=([0]="'kirk'" [1]="'bones'" [2]="'mr" [3]="spock'")

这不能按预期工作,因为命令替换未加引号,bash 将对输出执行分词。实际输出是否包含引号字符并不重要。参考手册中的3.5 Shell Expansions

您需要延迟数组分配,直到 shell 可以检查输出的内容。这可以eval完成,但对于变量赋值最好用declare完成:

$ declare -a "ary=($(echo '{"crew":[{"name":"kirk"},{"name":"bones"},{"name":"mr spock"}]}' | jq -r '.crew[].name | @sh'))"
$ declare -p ary
declare -a ary=([0]="kirk" [1]="bones" [2]="mr spock")

为了可读性,将其分成几个步骤:

$ jq_out=$(echo '{"crew":[{"name":"kirk"},{"name":"bones"},{"name":"mr spock"}]}' | jq -r '.crew[].name | @sh')
$ declare -a "ary=( $jq_out )"
$ declare -p ary
declare -a ary=([0]="kirk" [1]="bones" [2]="mr spock")

【讨论】:

【解决方案2】:

如果你的 bash 不支持 mapfile/readarray,你可以使用如下所示的成语:

echo $BASH_VERSION

while read -r name ; do
    ary+=("$name")
done < <(echo '{"crew":[{"name":"kirk"},{"name":"bones"},{"name":"mr spock"}]}' |
   jq -r '.crew[].name')

declare -p ary
输出
3.2.57(1)-release
declare -a ary='([0]="kirk" [1]="bones" [2]="mr spock")'

特别注意

  • $name 在数组更新行中被引用
  • 没有必要使用@sh,在这种情况下确实可能不是您想要的。

当然,如果 JSON 字符串可能包含嵌入的新行,则必须对上述内容进行相应调整,例如使用 NUL ("\u0000") 作为分隔符。

【讨论】:

  • 你会想要使用IFS= read -r name——这将完全逐字阅读该行。如果没有IFS=,则将删除前导/尾随空格。
猜你喜欢
  • 1970-01-01
  • 2016-03-14
  • 1970-01-01
  • 1970-01-01
  • 2019-06-02
  • 2012-02-07
  • 2021-03-15
  • 2021-01-13
  • 1970-01-01
相关资源
最近更新 更多