jq - 将JSON格式转换为Bash数组

5
我的问题类似于这个问题,但我需要做更多的事情,不太清楚该怎么做。
这是我的JSON字符串。
{
  "value": "1"
}
{
  "value": "3"
}
{
  "value": "4"
}

我需要编写一个BASH脚本来输出:
  • 2-如果序列中缺少数字
  • 5-如果序列没有缺少数字
在上面的示例中,json数组中缺少2。脚本应该返回2。如果数组中存在2,则应返回5。
我认为我可以使用while循环在bash中编写递增数字的逻辑,但是我卡在这个地方,无法弄清如何仅使用值将此JSON字符串转换为bash数组。
以下是我用于获取JSON输出的确切命令。这是从AWS获取具有特定标记的所有实例的AWS CLI命令。
readarray -t arr < <(aws ec2 describe-instances --region=us-east-1 --filters --filters "Name=tag:NodeType,Values=worker" --query "Reservations[].Instances[].Tags[]" | jq -r '.[] | select(.Key == "NodeNumber") | {value: .Value}')
printf '%s\n' "${arr[@]}"

上面的代码返回给我
{
  "value": "1"
}
{
  "value": "3"
}
{
  "value": "4"
}

然而,我需要将“value”字段作为bash数组获取。

你知道你要找的是哪个值吗? - randomir
在上面的例子中,我期望得到2。 - zeroweb
然而,如果示例是1,2,3,4,则我的bash脚本的输出应该是5。 - zeroweb
我的问题是脚本是否知道它需要查找“2”?如果是,如何知道?命令行参数吗? - randomir
不,脚本不知道要查找什么。我的逻辑是编写一个while()循环来循环遍历1到1000,以查看是否缺少任何这些值,如果确实缺少,则返回第一个缺失的值并退出循环。 - zeroweb
请参考 https://unix.stackexchange.com/a/630274/42620 中的方法将 JSON 数组转换为 Bash 数组,然后使用普通的 Shell 循环。 - jrw32982
4个回答

3

使用 jq 将您的 JSON 转换为 bash 数组:

$ readarray -t arr < <(jq '.value' file)
$ printf '%s\n' "${arr[@]}"
"1"
"3"
"4"

要修复您的扩展示例(确切的命令),只需不使用对象构造{value: .Value},而是仅使用.Value

$ readarray -t arr < <(aws ec2 describe-instances --region=us-east-1 --filters --filters "Name=tag:NodeType,Values=worker" --query "Reservations[].Instances[].Tags[]" | jq -r '.[] | select(.Key == "NodeNumber") | .Value')
$ printf '%s\n' "${arr[@]}"
1
3
4

注意到缺少双引号,因为-r选项现在仅打印原始字符串值,而不是原始JSON对象。

当你像这样填充arr的值后,你可以轻松地遍历它并执行测试,就像你在问题中描述的那样。


它没有返回任何东西。 - zeroweb
@getvivekv,我已经更新了我的答案,并提供了您确切命令的示例。这应该会给您所需的数组。 - randomir
你正在由管道诱发的子shell中执行readarray命令。你需要使用"lastpipe"选项,或者使用进程替代而不是管道(readarray -t arr < <(jq ...))。 - chepner
@chepner,糟糕,你是对的。我已经修复了它。谢谢! - randomir
@getvivekv,我在最初的答案中出现了一个错误,但现在已经修复了。 - randomir
显示剩余2条评论

2

首先,存储数据

假设您的原始数据存储为一个字符串在一个json变量中,可能使用here-document:

json=$(
    cat <<- EOF
        {
          "value": "1"
        }
        {
          "value": "3"
        }
        {
          "value": "4"
        }
EOF
)

Bash本身会做出合理的美化处理:
$ echo $json
{ "value": "1" } { "value": "3" } { "value": "4" }

将数据解析为Bash数组

有多种方法可以实现这一点。其中两种更明显的方法是使用jqgrep将值提取到Bash数组中,使用shell的简单数组表示法:

values=( `echo $json | jq .value` )
echo "${values[@]}"
"1" "3" "4"

unset values
values=$(egrep -o '[[:digit:]]+' <<< "$json")
echo "${values[@]}"
1
3
4

当然,实现这个任务的其他方法也是存在的,但这似乎是最简单的方法。不同的人可能会有不同的看法。

注意事项

Bash 数组的主要问题在于,如果你想将它们用于循环而不是直接索引,则需要使用诸如"${foo[@]}"这样的扩展形式进行引用,或者在未加引号时使用${bar[*]}。 一旦它们在数组中,只要你理解不同的扩展和引用规则,就可以轻松访问整个数组。


1
使用jq的“-s”命令行选项,以下内容符合我理解的要求,同时也进行了概括:
map(.value | tonumber)
| unique
| (1+max - min) as $length
| if $length > length then ([range(min;max+1)] - .)[]
  else max+1
  end

如果你只想要缺失值最小的值,那么请将 'if' 行上的 '[]' 替换为 [0]

我需要先找到缺失的值,如果序列中没有缺失的值,则找到下一个更高的值。我刚试过将其切换为“-s”,但这会破坏现有的代码,我将重新测试它。 - zeroweb

0
这可能会给你一些灵感:
#!/bin/bash

complete=true

while read value;do

    n=${n:-$value}

    if (( value != n ));then
            complete=false
            echo $n
            break
    fi

    let n++

done < <(jq -r '.value' json_file)
# or: done < <( command_that_outputs_json | jq -r '.value' )

$complete && echo $n

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接