bash:迭代通过索引select的JSON数组的成员

我使用jq来parsingJSON文件,将每个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"[]')) 

还是行不通。 任何想法? 任何帮助表示赞赏!


编辑:示例input数据

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

有一些相当大的改进空间。 我们从这里开始:

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

…首先,你实际上并不需要使用cat ; 它会降低性能,因为它强制jq从管道而不是从输入文件直接读取。 只要运行jq <"$INPUT"就会更健壮(或者更好的是,为了避免使用全部大写的名字,而这些名字被shell惯例和环境变量保留)。

其次,你需要引用所有的变量扩展,包括扩展输入文件的名字 – 否则,只要你的文件名包含空格就会得到错误。

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

第四,字符串替换成代码在任何语言中都是不好的做法 – 这种方式(本地等价于) Bobby表允许某人应该只能改变传入进程的数据,以提供被处理为可执行代码的内容(虽然在这种情况下,作为一个jq脚本,这是比在一个更全面的功能语言的任意代码执行危险性更低;但是,这可以允许额外的数据添加到输出)。

接下来,一旦让jq发出以换行符分隔的内容,根本不需要将其读入数组中:您可以迭代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") 

变量不会插入在单引号内。 使用双引号,并删除现有的引号。

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

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

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

你也可以修复猫的无用用法:

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

此外,请参阅@ CharlesDuffy的答案,为什么像这样分配数组是不安全的。

jq能够一次性提取结构,所以整个循环是多余的。 如果输入的JSON包含的记录数多于nvars值,则使用索引进行nvars

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

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

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

我花了太多时间才弄明白这一点。 我所看到的所有例子都是令人费解的,我必须把它们拼凑在一起。