将 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 数组的最可靠方法。
在 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
使用 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 数组的最可靠方法。