将 JQ NULL 分隔的输出存储在 bash 数组中

Storing JQ NULL-delimited output in bash array

在 bash 4.4.12 上使用 jq 1.5 和这个单行 IFS=_ read -r -a a < <(jq -ncj '["a","b","c"][]+"_"') ; printf '%s\n' "${a[@]}" 我得到一个正确分隔的输出

a

b

c

分别用于元素 a、b 和 c,但是如果我像这样使用空定界符尝试同样的事情:IFS= read -r -a a < <(jq -ncj '["a","b","c"][]+"\u0000"') ; printf '%s\n' "${a[@]}" 那么我将只得到一个包含

的数组元素

abc

为什么这不像预期的那样工作?

此外,如果您尝试 IFS= read -r -d '' -a a < <(jq -ncj '["a","b","c"][]+"\u0000"') ; printf '%s\n' "${a[@]},您会惊讶地得到一个只有第一个“a”元素的数组:

a

我的目标是找到一种无需使用任何类型的循环迭代元素的方法。

编辑:**readarray -d** 不是解决方案,因为我需要在 4.4

之前的 bash 中 运行 的一段代码

使用 readarray,它获得了 -d 类似于 bash 4.4 中 read 上的相同选项:

$ readarray -d $'[=10=]' -t a < <(jq -ncj '["a","b","c"][]+"\u0000"')
$ declare -p a
declare -a a=([0]="a" [1]="b" [2]="c")

-d '' 也可以;由于 shell 字符串以空字符结尾,因此从技术上讲,'' 是包含空字符的字符串。


没有 readarray -d 支持,您可以使用 while 循环和 read,这应该适用于任何版本的 bash:

a=()
while read -d '' -r item; do
    a+=("$item")
done < <( jq -ncj '["a","b","c"][]+"\u0000"' )

这是您所能做的最好的事情,除非您对数组元素有所了解,可以让您选择不属于任何元素的备用分隔符。

由于您没有使用 jq 的 -r 选项,问题是标题中提出的问题是否可能是 "XY" problem。如果目标只是将 JSON 值分配给 bash 数组,请考虑:

$ readarray -t a < <(jq -nc '["a","b","c"][]') ; printf '%s\n' "${a[@]}"
"a"
"b"
"c"

请注意,bash 数组值是可识别的 JSON 值(在本例中,JSON 字符串,带有 double-quotation 标记)。

更明显的是:

$ readarray -t a < <(jq -nc '["a\b","\"b\"","c"][]') ; printf '%s\n' "${a[@]}"
"a\b"
"\"b\""
"c"

比较使用带有 NUL 的 readarray 时发生的 "JSONarity" 的损失:

$ readarray -d "" a < <(jq -ncj '["a\b","\"b\"","c"][]+"\u0000"') ; printf '%s\n' "${a[@]}"
a\b
"b"
c

我假设您想改用空定界符而不是 _ 以提高脚本的可靠性。但是,读取 json 元素的最安全方法不是使用空分隔符,因为根据 RFC7159 (page 8),这是允许的 json 文本。例如。如果 ["a","b","c"] 看起来像 ["a","b\u0000","c"] 并且您要将空字符附加到每个字符串并使用空定界符解析它们,则“b”元素将分为两个单独的 bash 数组槽。

相反,考虑到换行符总是在 json-strings 内转义,例如使用jq -c 我建议依赖规范中说

的部分

"A string begins and ends with quotation marks."

考虑到这一点,我们可以定义:

jsonStripQuotes(){ local t0; while read -r t0; do t0="${t0%\"}"; t0="${t0#\"}"; printf '%s\n' "$t0"; done < <(jq '.');}

然后,例如

echo '["a\u0000 b\n","b\nnn","c d"]' | jq .[] | jsonStripQuotes

..应该安全地在单独的行上打印每个 json 字符串(附加扩展换行符),字符串中的所有换行符和空值都已转义。之后,我将 IFS 设置为仅换行符进行读取:

while IFS=$'\n' do read -r elem; Arr+=("$elem") ; done < <(echo '["a\u0000 b\n","b\nnn","c d"]' | jq .[] | stripJsonQuotes)

然后如果你想用换行符等打印它们扩展:

printf '%b' "${Arr[*]}"

我相信这是将 json 字符串解析为 bash 数组的最可靠方法。