在 Shell 脚本中遍历 JSON 数组

Posted

技术标签:

【中文标题】在 Shell 脚本中遍历 JSON 数组【英文标题】:Iterating through JSON array in Shell script 【发布时间】:2016-03-01 06:07:27 【问题描述】:

我在 data.json 文件中有如下 JSON 数据

[
  "original_name":"pdf_convert","changed_name":"pdf_convert_1",
  "original_name":"video_encode","changed_name":"video_encode_1",
  "original_name":"video_transcode","changed_name":"video_transcode_1"
]

我想遍历数组并提取循环中每个元素的值。我看到了jq。我发现很难用它来迭代。我该怎么做?

【问题讨论】:

看起来jq 有一个foreach 命令,你试过吗? 老实说,我认为您会对简单的 Python 脚本更加满意。你甚至可以使用 heredoc 语法将它嵌入到你的 shell 脚本中。 你能举一个将python嵌入到shell脚本中的例子吗? 【参考方案1】:

尝试围绕这个示例构建它。 (来源:原站)

例子:

jq '[foreach .[] as $item ([[],[]]; if $item == null then [[],.[0]]     else [(.[0] + [$item]),[]] end; if $item == null then .[1] else empty end)]'

Input [1,2,3,4,null,"a","b",null]

Output [[1,2,3,4],["a","b"]]

【讨论】:

最初的问题含糊不清,但我认为foreach 对于用户想要的东西根本没有必要。【参考方案2】:

只需使用会返回数组中每个项目的过滤器。然后循环遍历结果,只需确保使用紧凑输出选项 (-c),以便将每个结果放在一行中并作为循环中的一项处理。

jq -c '.[]' input.json | while read i; do
    # do stuff with $i
done

【讨论】:

A for 循环遍历以空格分隔的单词,而不是行。 是的,你是对的,但在这种特定情况下,它本来可以,因为任何对象中都没有空格。但是思路还是一样,循环机制可能是错误的选择。 jq 输出流,因此您不会逐行或逐项进行。 如果您的输出包含空格,您需要将 IFS 设置为换行符,例如使用 Bash IFS=$'\n' 为我工作(Mac 上的 Big Sur)。这是我到目前为止得到的:echo "$res" | jq -c -r '.[]' | while read item; do val=$(jq -r '.value' <<< "$item") echo "Value: $val" done【参考方案3】:

此线程中较早的答案建议使用 jq 的foreach,但这可能比需要的复杂得多,尤其是考虑到所述任务。具体来说,foreach(和reduce)适用于需要累积结果的某些情况。

在许多情况下(包括某些最终需要减少步骤的情况),最好使用.[]map(_)。后者只是 [.[] | 的另一种写法。 _] 所以如果你打算使用 jq,理解 .[] 只是创建一个 stream 值真的很有用。 例如,[1,2,3] | .[] 生成三个值的流。

举一个简单的 map-reduce 例子,假设你想找到一个字符串数组的最大长度。一种解决方案是[ .[] | length] | max

【讨论】:

【参考方案4】:

jq 有一个 shell 格式化选项:@sh

您可以使用以下内容将您的 json 数据格式化为 shell 参数:

cat data.json | jq '. | map([.original_name, .changed_name])' | jq @sh

输出将如下所示:

"'pdf_convert' 'pdf_convert_1'"
"'video_encode' 'video_encode_1'",
"'video_transcode' 'video_transcode_1'"

要处理每一行,我们需要做几件事:

将 bash for 循环设置为读取整行,而不是在第一个空格处停止(默认行为)。 去掉每一行的封闭双引号,这样每个值都可以作为参数传递给处理每一行的函数。

要在 bash for 循环的每次迭代中读取整行,请设置 IFS 变量,如 this answer 中所述。

为了去掉双引号,我们将使用 xargs 通过 bash shell 解释器运行它:

stripped=$(echo $original | xargs echo)

综上所述,我们有:

#!/bin/bash

function processRow() 
  original_name=$1
  changed_name=$2

  # TODO


IFS=$'\n' # Each iteration of the for loop should read until we find an end-of-line
for row in $(cat data.json | jq '. | map([.original_name, .changed_name])' | jq @sh)
do
  # Run the row through the shell interpreter to remove enclosing double-quotes
  stripped=$(echo $row | xargs echo)

  # Call our function to process the row
  # eval must be used to interpret the spaces in $stripped as separating arguments
  eval processRow $stripped
done
unset IFS # Return IFS to its original value

【讨论】:

您可以使用--raw-output-r 标志来排除封闭的双引号,而不必“剥离封闭的双引号”,将jq @sh 替换为jq -r @sh 您(目前)不需要通过第二个 jq 的 shell 管道;只需在 jq 管道中附加 | @sh 就可以了。如jq -r '. | map(blah) | @sh'【参考方案5】:

通过利用 Bash 数组的强大功能,您可以执行以下操作:

# read each item in the JSON array to an item in the Bash array
readarray -t my_array < <(jq -c '.[]' input.json)

# iterate through the Bash array
for item in "$my_array[@]"; do
  original_name=$(jq '.original_name' <<< "$item")
  changed_name=$(jq '.changed_name' <<< "$item")
  # do your stuff
done

【讨论】:

“Bash 数组的力量!⚡️” - 人太多了。 macOS 用户请注意 - 由于 Apple 由于许可而坚持使用旧版本的 bash(当前为 v3.2.57),因此这将无法“开箱即用”。您可以使用自制软件获取最新版本。您需要将较新的版本设置为默认 shell,或者将脚本设置为通过 shebang 显式使用它 很高兴知道!这一定是 macOS 如此切换到 ZSH 的原因。 如果从变量中读取:readarray -t my_array &lt; &lt;(jq -c '.[]' &lt;&lt;&lt; $input_json) 这只是开箱即用的解决方案。所有其他都是需要认真纠正才能工作的概念!【参考方案6】:

来自Iterate over json array of dates in bash (has whitespace)

items=$(echo "$JSON_Content" | jq -c -r '.[]')
for item in $items[@]; do
    echo $item
    # whatever you are trying to do ...
done

【讨论】:

为什么echo $items[1] 不显示结果? 对我不起作用(Mac Big Sur)。具有多个项目的列表只有一次循环迭代。不过,@JeffMercado 的回答确实有效。【参考方案7】:

这是我到目前为止所做的

 arr=$(echo "$array" | jq -c -r '.[]')
            for item in $arr[@]; do
               original_name=$(echo $item | jq -r '.original_name')
               changed_name=$(echo $item | jq -r '.changed_name')
              echo $original_name $changed_name
            done

【讨论】:

当访问一个key的值时,而不是uisng. original_name不带引号,应该是original_name =$(echo $item | jq -r '.original_name')吗?还有,为什么=前面有个空格?

以上是关于在 Shell 脚本中遍历 JSON 数组的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Zoho CRM Deluge 脚本中使用 for each 循环遍历 JSON 数组

shell脚本中的数组排序

Shell脚本------数组

请教一个shell脚本 输出到数组

shell脚本——数组的应用及排序算法(冒泡直接反转希尔排序)

EditorGUILayout类怎么操作目标脚本的数组