0%

jq处理json

jq 处理 JSON 详解:从安装到高级应用

在日常工作中,JSON 格式的日志、配置文件或接口响应非常常见,jq 作为轻量级的命令行 JSON 处理器,能高效完成过滤、提取、分组、计数等操作,是处理 JSON 数据的必备工具。本文将从安装 jq 开始,围绕 JSON 数组和 “每行一个 JSON 对象”(JSON Lines)两种常见格式,详解 jq 的核心用法。

安装 jq

jq 不是系统默认安装的工具,需手动安装,以下是主流操作系统的安装方法:

Linux 系统

(1)Debian/Ubuntu 系列
1
sudo apt-get update && sudo apt-get install -y jq
(2)CentOS/RHEL 系列
1
2
3
4
5
6
# CentOS 7 及以上(需启用 EPEL 源)
sudo yum install -y epel-release
sudo yum install -y jq

# CentOS 8 / Rocky Linux 等(使用 dnf)
sudo dnf install -y jq
(3)Fedora
1
sudo dnf install -y jq
(4)Arch Linux
1
sudo pacman -S jq

macOS 系统

(1)使用 Homebrew(推荐)
1
brew install jq
(2)使用 MacPorts
1
sudo port install jq

Windows 系统

(1)使用 Chocolatey 包管理器
1
choco install jq -y
(2)手动下载

jq 官方网站 下载 Windows 版本的可执行文件,添加到系统环境变量 PATH 中即可。

验证安装

安装完成后,执行以下命令验证:

1
2
jq --version
# 输出示例:jq-1.6(版本号可能因系统而异)

jq 基础:语法与核心概念

基本语法

1
jq [选项] '过滤表达式' [输入文件]
  • 输入:可以是 JSON 文件(.json)、JSON Lines 文件(.jsonl,每行一个 JSON 对象),或通过管道传递的 JSON 流。
  • 过滤表达式jq 的核心,用于描述对 JSON 数据的处理逻辑(如筛选、提取、转换等)。
  • 输出:处理后的 JSON 数据(默认格式化输出,便于阅读)。

核心概念

  • JSON 结构jq 支持处理 JSON 对象({key: value})和数组([item1, item2])。
  • 字段访问:通过 .字段名 访问对象字段(如 .name 获取 name 字段);通过 .[索引] 访问数组元素(如 .[0] 获取数组第一个元素)。
  • 管道操作:用 | 连接多个处理步骤(如先筛选再分组:select(...) | group_by(...))。

处理 JSON 数组:完整数组结构

JSON 数组是最常见的格式(整个文件为一个数组,如 [{"a":1}, {"a":2}])。以下通过实例讲解筛选、分组、聚合等操作。

基础筛选:select()

select(条件) 用于筛选符合条件的元素,条件为布尔表达式(如字段相等、数值比较等)。

示例:假设有 data.json,内容为:

1
2
3
4
5
6
[
{"app": "user", "status": "success", "date": "20241212"},
{"app": "order", "status": "fail", "date": "20241212"},
{"app": "user", "status": "success", "date": "20241213"},
{"app": "user", "status": "fail", "date": "20241212"}
]

筛选 date20241212 的记录:

1
jq '.[] | select(.date == "20241212")' data.json
  • .[]:遍历数组中的每个元素(将数组 “展开” 为单个对象)。
  • select(.date == "20241212"):保留 date 字段等于 20241212 的对象。

输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"app": "user",
"status": "success",
"date": "20241212"
}
{
"app": "order",
"status": "fail",
"date": "20241212"
}
{
"app": "user",
"status": "fail",
"date": "20241212"
}

分组与计数:group_by() + map()

group_by(字段) 按指定字段对数组元素分组(返回嵌套数组,每组为相同字段值的元素集合);map(表达式) 对分组后的数组进行转换(如计算每组长度即计数)。

示例:按 appstatus 分组,统计 20241212 当天各组合的数量:

1
2
3
4
5
6
7
8
jq '
map(select(.date == "20241212")) | # 先筛选日期符合的记录
group_by(.app, .status) | # 按app和status分组
map({ # 转换每组为{key, count}结构
key: {app: .[0].app, status: .[0].status},
count: length
})
' data.json

解析

  • map(select(...)):对数组中每个元素应用筛选,返回筛选后的新数组(避免用 .[] 展开,否则后续无法分组)。
  • group_by(.app, .status):按 appstatus 的组合分组,结果为嵌套数组(如 [[user-success元素], [order-fail元素], ...])。
  • map(...):遍历每个分组,用 .[0].app 获取组内第一个元素的 app(因分组后组内元素该字段相同),length 为组内元素数量(计数)。

输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[
{
"key": {
"app": "order",
"status": "fail"
},
"count": 1
},
{
"key": {
"app": "user",
"status": "fail"
},
"count": 1
},
{
"key": {
"app": "user",
"status": "success"
},
"count": 1
}
]

处理 JSON Lines:每行一个 JSON 对象

日志文件常采用 “JSON Lines” 格式(.jsonl),即每行一个独立的 JSON 对象(而非整个文件为一个数组)。此时需先用 jq-s 选项将所有行合并为一个数组,再进行聚合操作。

-s 选项:合并多行为数组

-s--slurp)选项会将所有输入行读入一个大数组(每行一个元素),从而支持跨行进分组、聚合。

示例data.jsonl 内容(每行一个对象):

1
2
3
4
{"app": "user", "status": "success", "date": "20241212"}
{"app": "order", "status": "fail", "date": "20241212"}
{"app": "user", "status": "success", "date": "20241213"}
{"app": "user", "status": "fail", "date": "20241212"}

合并为数组并查看:

1
jq -s '.' data.jsonl  # -s 将所有行合并为一个数组

输出

1
2
3
4
5
6
[
{"app": "user", "status": "success", "date": "20241212"},
{"app": "order", "status": "fail", "date": "20241212"},
{"app": "user", "status": "success", "date": "20241213"},
{"app": "user", "status": "fail", "date": "20241212"}
]

聚合操作:分组计数

对 JSON Lines 文件进行分组计数,步骤与 JSON 数组类似(先合并为数组,再筛选、分组)。

示例:统计 data.jsonl20241212 当天各 appstatus 的数量:

1
2
3
4
5
6
7
8
9
jq -s '
map(select(.date == "20241212")) | # 筛选日期符合的记录
group_by([.app, .status]) | # 按[app, status]组合分组(数组形式)
map({
app: .[0].app,
status: .[0].status,
count: length
})
' data.jsonl

说明

  • -s 确保所有行被合并为一个数组,后续 mapgroup_by 可作用于整个数据集。
  • group_by([.app, .status])group_by(.app, .status) 效果相同,均按两个字段组合分组。

输出:与 JSON 数组示例相同(按 appstatus 分组的计数结果)。

进阶操作:提取、转换与条件逻辑

提取指定字段

{新字段名: .原字段名} 提取需要的字段,简化输出。

示例:从 data.json 中提取 appdate 字段:

1
jq '.[] | {应用: .app, 日期: .date}' data.json

输出

1
2
3
{ "应用": "user", "日期": "20241212" }
{ "应用": "order", "日期": "20241212" }
...

条件转换:if-else

根据字段值进行条件转换(如将 status 转换为中文)。

示例:将 statussuccess 转为 “成功”,fail 转为 “失败”:

1
2
3
4
jq '.[] | {
app: .app,
状态: if .status == "success" then "成功" else "失败" end
}' data.json

嵌套 JSON 处理

对于嵌套结构(如 {a: {b: 1}}),用 .a.b 访问深层字段。

示例:处理嵌套 JSON:

1
2
3
4
5
// nested.json
[
{"app": "pay", "detail": {"code": 200, "msg": "ok"}},
{"app": "pay", "detail": {"code": 500, "msg": "error"}}
]

提取 appdetail.code

1
jq '.[] | {app: .app, code: .detail.code}' nested.json

实用技巧与常见场景

1. 格式化输出

默认输出已格式化,若需紧凑格式(无缩进),用 -c 选项:

1
jq -c '.[] | select(.date == "20241212")' data.json  # 紧凑输出

2. 日志分析场景

结合管道处理实时日志流(如从 tail 输出中筛选特定日志):

1
tail -f app.log | jq -c 'select(.level == "error")'  # 实时打印错误级别日志

3. 计数去重

统计某个字段的去重值及出现次数:

1
2
3
4
jq -s '
group_by(.app) |
map({app: .[0].app, count: length})
' data.jsonl # 统计每个app的出现次数

欢迎关注我的其它发布渠道

表情 | 预览
快来做第一个评论的人吧~
Powered By Valine
v1.3.10