在 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
【讨论】:
Afor
循环遍历以空格分隔的单词,而不是行。
是的,你是对的,但在这种特定情况下,它本来可以,因为任何对象中都没有空格。但是思路还是一样,循环机制可能是错误的选择。
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 < <(jq -c '.[]' <<< $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 数组