6

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

4 回答 4

15

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

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

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

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

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

第四,在任何语言中,将字符串替换为代码都是不好的做法——这种做法在于(本地等价于)Bobby Tables,允许应该只能更改传递给您的进程的数据的人提供作为可执行代码处理的内容(尽管在这种情况下,作为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在分配之前显式设置并禁用全局扩展:

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")
于 2015-01-16T22:30:20.683 回答
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 的答案,以详细解释为什么像这样分配给数组是不安全的。

于 2015-01-16T22:01:37.937 回答
2

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

while IFS= read -r; do
  echo "$REPLY"
done < <(echo "$MY_VAR" | jq -r '.[]')
于 2017-06-21T17:31:35.563 回答
1

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

jq -r '."config-vars"[]."var1"' "$INPUT" |
head -n "${#nvars[@]}"  # If you need just the #nvars first values
于 2015-11-24T06:12:39.677 回答