bash:迭代索引选择的JSON数组的成员

时间:2015-01-16 21:58:04

标签: json linux bash shell jq

我正在使用jq来解析JSON文件,将一系列中的每个JSON数组提取到一个shell数组中。

我目前的代码如下所示:

for ((i = 0; i < ${#nvars[@]}; i++)); do
    v1=($(cat $INPUT | jq '."config"[i]."var1"[]'))
    echo $v1
done

错误消息:

error: i is not defined

我也替换了

v1=($(cat $INPUT | jq '."config"[i]."var1"[]'))

v1=($(cat $INPUT | jq '."config"[$i]."var1"[]'))

仍然无法正常工作。任何的想法?任何帮助表示赞赏!


编辑:示例输入数据

{
    "config-vars":[
        {
            "var1":["v1","v2"],
            "var2":""
        },
        {
            "var1":["v3",""],
            "var2":"v4"
        }
    ]
}

4 个答案:

答案 0 :(得分:15)

还有一些改进空间。让我们从这里开始:

v1=($(cat $INPUT | jq '."config"[$i]."var1"[]'))

......首先,您实际上并不需要使用cat;它会降低您的性能,因为它会强制jq从管道而不是直接从您的输入文件中读取。只运行jq <"$INPUT"会更健壮(或更好,<"$input",以避免使用全大写名称,这些名称是由shell内置函数和环境变量按惯例保留的。)

其次,您需要引用所有变量扩展,包括扩展输入文件的名称 - 否则,只要您的文件名包含空格,就会出现错误。

第三,array=( $(stuff) )stuff的输出拆分为 IFS 中的所有字符,并将该拆分的结果展开为一系列glob表达式(所以如果输出包含*.txt,并且您在包含文本文件的目录中运行此脚本,您将获得结果数组中这些文件的名称。仅在新行上拆分意味着您可以正确解析多字符串,并且必须禁用glob扩展才能在存在glob字符的情况下可靠地使用此技术。一种方法是在运行此命令之前设置IFS=$'\n'并运行set -h;另一种方法是将命令的输出重定向到while read循环(如下所示)。

第四,代码中的字符串替换是任何语言的不良做法 - 这种方式(本地等价物)Bobby Tables,允许某些人只能改变传递给你的数据提供作为可执行代码处理的内容的过程(虽然在这种情况下,作为jq脚本,这比在更全功能的语言中执行任意代码更不危险;但是,这可以允许额外的数据到被添加到输出中。)

接下来,一旦您获得jq发布换行符分隔的内容,您根本不需要将其读入数组:您可以按原样迭代内容。 ; s从jq写入并读入您的shell,从而防止shell需要分配内存来缓冲该内容:

while IFS= read -r; do
  echo "read content from jq: $REPLY"
done < <(jq -r --arg i "$i" '.config[$i | tonumber].var1[]' <"$input")

最后 - 让我们说想要使用数组。有两种方法可以避免陷阱。一种是明确设置IFS并在赋值前禁用glob扩展:

IFS=$'\n' # split only on newlines
set -f
result=( $(jq -r ... <"$input") )

另一种是用循环分配给你的数组:

result=( )
while IFS= read -r; do
  result+=( "$REPLY" )
done < <(jq -r ... <"$input")

...或者,正如@JohnKugelman所建议的,使用read -a在一次操作中读取整个数组:

IFS=$'\n' read -r -d '' -a result < <(jq -r ... <"$input")

答案 1 :(得分:3)

变量不在单引号内插值。请改用双引号,并删除现有的引号。

v1=($(cat $INPUT | jq ".config[$i].var1[]"))

或使用--arg选项,然后您可以坚持使用单引号。

v1=($(cat $INPUT | jq --arg i "$i" '.config[$i].var1[]'))

您还可以修复cat的无用用法:

v1=($(jq ".config[$i].var1[]" "$INPUT"))

另外,请参阅@ CharlesDuffy的回答,详细解释为什么分配给这样的数组是不安全的。

答案 2 :(得分:1)

jq能够一次性提取结构,因此整个循环是多余的。如果输入JSON包含的记录多于nvars中的值,请使用索引进行切割。

jq -r '."config-vars"[]."var1"' "$INPUT" |
head -n "${#nvars[@]}"  # If you need just the #nvars first values

答案 3 :(得分:1)

如果您已将某些JSON的结果存储到名为$ MY_VAR的变量中:

while IFS= read -r; do
  echo “$REPLY”
done < <(echo $MY_VAR | jq -r ‘.[]‘)

我花了很长时间才弄明白这一点。我见过的所有例子都令人费解,我不得不把它拼凑起来。