「Shell」- 处理 JSON 数据

  CREATED BY JENKINSBOT

问题描述

我们需要在 Shell 中处理 JSON 数据,比如我们请求某个 HTTP 接口,接口返回 JSON 数据,而我们需要从中提取某个字段。当然,我们可以使用 sed grep awk 这些工具,但是这些工具只能进行简单处理。如果我们想要生成结构化数据,这些工具是不够的。我们需要处理 JSON 的专用工具。

经过 Google 查找 jq(stedolan/jq: Command-line JSON processor),用于处理 JSON 数据的 Shell 命令行工具。

该笔记将记录:在 Shell 中,如何使用 jq 命令处理 JSON 数据,以及常见问题处理。

解决方案

安装命令

通过包管理器安装。很多 Linux 发行版中都带有该工具:

# Ubuntu 18.04 LTS
apt-get install -y jq

或者,使用源码编译安装,详细的安装过程,参考 jq/README.md at master · stedolan/jq 文件。这里简单记录:

git submodule update --init # if building from git to get oniguruma (正则表达式库)
autoreconf -fi              # if building from git
./configure --with-oniguruma=builtin
make -j8
make check

常见示例

根据属性值过滤:

jq '.result | select(.location=="Stockholm")'                                   

判断某个 KEY 是否存在。比如,判断在 result 是否包含 .property_history 字段(KEY),存在则取其值:

jq '.result | select(.property_history != null) | .property_history'           

判断字段的值是否以某个字符串开始:

jq -r '.[]|select(.hostname | startswith("abcd"))' jjjj.json

# 判断是否包含某个字符串
jq -r '.[]|select(.hostname | contains("abcd"))' jjjj.json

修改 JSON 内容:

jq '.clusters[].name = 1231231 | .users[].name = 123456' config.json

传递变量:

jq --arg user "$SOME_USER" '.something.user|="\($user) did a thing"'

常用语句

if / foreach

if (.clusters | length) > 1 then
    reduce range(0; .clusters | length) as $idx (.; .clusters[$idx].name = $f + "/" + .clusters[$idx].name) | 
    reduce range(0; .contexts | length) as $idx (.; .contexts[$idx].context.cluster = $f + "/" + .contexts[$idx].context.cluster)
else 
    .clusters[].name = $f | 
    .contexts[].context.cluster = $f
end

输出自定义文本、字符串拼接(输出可执行的命令)

Concat 2 fields in JSON using jq – Stack Overflow

我们希望 jq 可以输出可以执行的 Shell 命令,这里就涉及字符串拼接:

echo '{"channel": "youtube", "profile_type": "video", "member_key": "hello"}' \
 | jq '{channel: (.profile_type + "." + .channel)}'

如上示例,类似某些编程语言,使用 + 进行字符串拼接。

将输出作为命令参数(执行命令)

Using jq with bash to run command for each object in array – Stack Overflow

如下示例,将输出使用 TAB 分割字段,再传入 read 命令,便可在命令中直接使用

jq -r '.[]|[.user, .date, .email] | @tsv' |
  while IFS=$'\t' read -r user date email; do
    cmd -u "$user" -d "$date" -e "$email"
  done

参考文献

man 1 jq, version
stedolan/jq: Command-line JSON processor
Cookbook · stedolan/jq Wiki
json – How to check for presence of ‘key’ in jq before iterating over the values – Stack Overflow
json – jq – select an attribute beginning with a string – Unix & Linux Stack Exchange
How do I update a single value in a json document using jq? – Stack Overflow
json – How do I assign something to a JQ variable I’ve already set in JQ? – Stack Overflow
json – Select objects based on value of variable in object using jq – Stack Overflow