bash:遍历由索引选择的JSON数组的成员 [英] bash: Iterating over members of a JSON array selected by index

查看:107
本文介绍了bash:遍历由索引选择的JSON数组的成员的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm using jq to parse a JSON file, extracting each JSON array in a series into a shell array.

我当前的代码如下:

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"[]'))

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

still not working. Any idea? Any help is appreciated!

样本输入数据

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

推荐答案

还有很多改进的余地.让我们从这里开始:

There's a fair bit of room for improvement. Let's start here:

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

...首先,您实际上不需要使用cat;这会降低性能,因为它强制jq从管道而不是直接从输入文件读取.仅运行jq <"$INPUT"会更健壮(或者最好使用<"$input",以避免使用全大写名称,这是外壳程序内置函数和环境变量的常规约定).

...first, you don't actually need to use cat; it's slowing your performance, because it forces jq to read from a pipe rather than from your input file directly. Just running jq <"$INPUT" would be more robust (or, better, <"$input", to avoid using all-uppercase names, which are reserved by convention for shell builtins and environment variables).

第二,您需要引用所有变量扩展名,包括输入文件名的扩展名-否则,只要文件名包含空格,就会出现bug.

Second, you need to quote all variable expansions, including the expansion of the input file's name -- otherwise, you'll get bugs whenever your filename contains spaces.

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

Third, array=( $(stuff) ) splits the output of stuff on all characters in IFS, and expands the results of that splitting as a series of glob expressions (so if the output contains *.txt, and you're running this script in a directory that contains text files, you get the names of those files in your result array). Splitting on newlines only would mean you could correctly parse multi-word strings, and disabling glob expansion is necessary before you can use this technique reliably in the presence of glob characters. One way to do this is to set IFS=$'\n' and run set -h before running this command; another is to redirect the output of your command into a while read loop (shown below).

第四,将字符串替换为代码在任何语言中都是不好的做法-这种方式(等效于本地) Bobby Tables ,允许原本只能更改传递到您的进程中的数据的人提供作为可执行代码处理的内容(尽管在这种情况下,作为jq脚本,这比任意代码的危险性要小)以更全功能的语言执行;不过,这可以允许将额外的数据添加到输出中.

Fourth, string substitution into code is bad practice in any language -- that way lies (local equivalents to) Bobby Tables, allowing someone who's supposed to be able to only change the data passed into your process to provide content which is processed as executable code (albeit, in this case, as a jq script, which is less dangerous than arbitrary code execution in a more full-featured language; still, this can allow extra data to be added to the output).

接下来,一旦让jq发出换行符分隔的内容,就根本不需要将其读取到数组中:您可以遍历从jq写入的内容并将其读入您的外壳程序,从而避免外壳程序需要分配内存来缓冲该内容:

Next, once you're getting jq to emit newline-separated content, you don't need to read it into an array at all: You can iterate over the content as it's written from jq and read into your shell, thus preventing the shell from needing to allocate memory to buffer that content:

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

最后-假设您要做要使用数组.有两种方法可以避免陷阱.一种是显式设置IFS并在分配之前禁用全局扩展:

Finally -- let's say you do want to work with an array. There are two ways to do this that avoid pitfalls. One is to set IFS explicitly and disable glob expansion before the assignment:

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

另一种方法是使用循环将其分配给您的数组:

The other is to assign to your array with a loop:

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

...或者按照@JohnKugelman的建议,使用read -a一次操作读取整个数组:

...or, as suggested by @JohnKugelman, to use read -a to read the whole array in one operation:

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

这篇关于bash:遍历由索引选择的JSON数组的成员的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆