将使用 jq 解析的数组分配给 Bash 脚本数组

İlk*_*rci 4 arrays bash jq

我用 jq 解析了一个 json 文件,如下所示:

# cat test.json | jq '.logs' | jq '.[]' | jq '._id' | jq -s
Run Code Online (Sandbox Code Playgroud)

它返回一个这样的数组: [34,235,436,546,.....]

使用 bash 脚本我描述了一个数组:

# declare -a msgIds = ...
Run Code Online (Sandbox Code Playgroud)

当我将上面给出的数组传递给这个数组时,这个数组使用()而不是[]so 它将不起作用。

([324,32,45..])这会导致问题。如果我删除jq -s,则会形成一个只有 1 个成员的数组。

有没有办法解决这个问题?

Gri*_*vit 12

要正确解析可能包含换行符(以及任何其他任意(非 NUL)字符)的值,请使用 jq 的@sh过滤器生成以空格分隔的带引号的字符串,并使用 Bash 的过滤器declare -a将带引号的字符串解析为数组元素。(无需预处理)

foo.json

{"data": ["$0", " \t\n", "*", "\"", ""]}
Run Code Online (Sandbox Code Playgroud)
str=$(jq -r '.data | @sh' foo.json)
declare -a arr="($str)"   # must be quoted like this
Run Code Online (Sandbox Code Playgroud)
declare -p arr
# declare -a arr=([0]="\$0" [1]=$' \t\n' [2]="*" [3]="\"" [4]="")
Run Code Online (Sandbox Code Playgroud)

更新:jq1.7(2023-09)

从版本1.7开始,jq有一个--raw-output0选项,使其能够输出以 null 结尾的字符串,这些字符串可以像平常一样读入数组:

mapfile -d '' arr < <(jq --raw-output0 '.data[]' foo.json)
wait "$!"  # use in bash-4.4+ to get exit status of the process substitution
Run Code Online (Sandbox Code Playgroud)

关于 NUL 字符的注意事项

JSON 字符串可以包含 NUL 字符,而 shell 变量则不能。如果您的 JSON 输入可能包含 NUL,您可能需要添加一些特殊处理。

  • 使用@sh过滤器时,JSON 字符串中的 NUL 字符将被静默替换为序列\0。请注意,这使得 JSON 字符串"\\0"无法"\u0000"区分。

  • 使用该--raw-output0选项时,NUL 字符将触发错误并jq以退出状态 5 终止。

读取多个/嵌套数组

可以将过滤@sh器结合起来,--raw-output0一次可靠地读取多个数组(或单个嵌套数组),因为它将生成一个以 NUL 分隔的空格分隔的带引号字符串列表。

json='[[1,2],[3,4]]' i=0
while read -r -d ''; do
    declare -a "arr$((i++))=($REPLY)"
done < <(jq --raw-output0 '.[]|@sh' <<<$json)
Run Code Online (Sandbox Code Playgroud)
for ((n=0; n<i; n++)); { declare -p "arr$n"; }
# declare -a arr0=([0]="1" [1]="2")
# declare -a arr1=([0]="3" [1]="4")
Run Code Online (Sandbox Code Playgroud)

  • 这是最正确的答案,因为即使使用“set -e”,它也会导致正确的错误处理。 (3认同)

Din*_*lam 10

我们可以通过两种方式解决这个问题。他们是:

输入字符串:

// test.json
{
    "keys": ["key1","key2","key3"]
}
Run Code Online (Sandbox Code Playgroud)

方法一:

1) 使用jq -r(输出原始字符串,而不是 JSON 文本)。

KEYS=$(jq -r '.keys' test.json)
echo $KEYS
# Output: [ "key1", "key2", "key3" ]
Run Code Online (Sandbox Code Playgroud)

2)使用@sh(将输入字符串转换为一系列以空格分隔的字符串)。它从字符串中删除方括号[]、逗号(,)。

KEYS=$(<test.json jq -r '.keys | @sh')
echo $KEYS
# Output: 'key1' 'key2' 'key3'
Run Code Online (Sandbox Code Playgroud)

3)tr用于从字符串输出中删除单引号。要删除特定字符,请使用tr.

KEYS=$((<test.json jq -r '.keys | @sh')| tr -d \') 
echo $KEYS
# Output: key1 key2 key3
Run Code Online (Sandbox Code Playgroud)

4)我们可以通过将字符串输出放在圆括号()中来将逗号分隔的字符串转换为数组。它也称为复合赋值,我们用一堆值声明数组。

ARRAYNAME=(value1 value2  .... valueN)
Run Code Online (Sandbox Code Playgroud)
#!/bin/bash
KEYS=($((<test.json jq -r '.keys | @sh') | tr -d \'\"))

echo "Array size: " ${#KEYS[@]}
echo "Array elements: "${KEYS[@]}

# Output: 
# Array size:  3
# Array elements: key1 key2 key3
Run Code Online (Sandbox Code Playgroud)

方法二:

1)jq -r用于获取字符串输出,然后用于tr删除方括号、双引号和逗号等字符。

#!/bin/bash
KEYS=$(jq -r '.keys' test.json  | tr -d '[],"')
echo $KEYS

# Output: key1 key2 key3
Run Code Online (Sandbox Code Playgroud)

2)然后我们可以通过将我们的字符串输出放在圆括号()中来将逗号分隔的字符串转换为数组。

#!/bin/bash
KEYS=($(jq -r '.keys' test.json  | tr -d '[]," '))

echo "Array size: " ${#KEYS[@]}
echo "Array elements: "${KEYS[@]}

# Output:
# Array size:  3
# Array elements: key1 key2 key3
Run Code Online (Sandbox Code Playgroud)

  • @yfpb,`@sh`是一个jq构造,而不是一个shell命令;您需要确保它是 jq 查询的一部分。 (2认同)

hob*_*bbs 8

使用jq -r输出字符串“原材料”,没有JSON格式,并使用@sh格式来格式化你的结果作为外壳消费的字符串。根据 jq 文档:

@sh:

输入已转义,适用于 POSIX shell 的命令行。如果输入是数组,则输出将是一系列以空格分隔的字符串。

所以可以做例如

msgids=($(<test.json jq -r '.logs[]._id | @sh'))
Run Code Online (Sandbox Code Playgroud)

并得到你想要的结果。


pea*_*eak 8

来自 jq 常见问题解答(https://github.com/stedolan/jq/wiki/FAQ):

:如何将 jq 生成的 JSON 文本流转换为相应值的 bash 数组?

答:一种选择是使用映射文件(又名 readarray),例如:

mapfile -t array <<< $(jq -c '.[]' input.json)
Run Code Online (Sandbox Code Playgroud)

另一种可能指示在其他 shell 中执行的操作的替代方法是在 while 循环中使用 read -r。以下 bash 脚本使用 JSON 文本填充数组 x。关键点是 -c 选项的使用,以及 bash 习惯用法的使用while read -r value; do ... done < <(jq .......)

#!/bin/bash
x=()
while read -r value
do
  x+=("$value")
done < <(jq -c '.[]' input.json)
Run Code Online (Sandbox Code Playgroud)