jq处理json
jq 处理 JSON 详解:从安装到高级应用
在日常工作中,JSON 格式的日志、配置文件或接口响应非常常见,jq 作为轻量级的命令行 JSON 处理器,能高效完成过滤、提取、分组、计数等操作,是处理 JSON 数据的必备工具。本文将从安装 jq 开始,围绕 JSON 数组和 “每行一个 JSON 对象”(JSON Lines)两种常见格式,详解 jq 的核心用法。
安装 jq
jq 不是系统默认安装的工具,需手动安装,以下是主流操作系统的安装方法:
Linux 系统
(1)Debian/Ubuntu 系列
sudo apt-get update && sudo apt-get install -y jq
(2)CentOS/RHEL 系列
# 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
sudo dnf install -y jq
(4)Arch Linux
sudo pacman -S jq
macOS 系统
(1)使用 Homebrew(推荐)
brew install jq
(2)使用 MacPorts
sudo port install jq
Windows 系统
(1)使用 Chocolatey 包管理器
choco install jq -y
(2)手动下载
从 jq 官方网站 下载 Windows 版本的可执行文件,添加到系统环境变量 PATH 中即可。
验证安装
安装完成后,执行以下命令验证:
jq --version
# 输出示例:jq-1.6(版本号可能因系统而异)
jq 基础:语法与核心概念
基本语法
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,内容为:
[
{"app": "user", "status": "success", "date": "20241212"},
{"app": "order", "status": "fail", "date": "20241212"},
{"app": "user", "status": "success", "date": "20241213"},
{"app": "user", "status": "fail", "date": "20241212"}
]
筛选 date 为 20241212 的记录:
jq '.[] | select(.date == "20241212")' data.json
.[]:遍历数组中的每个元素(将数组 “展开” 为单个对象)。select(.date == "20241212"):保留date字段等于20241212的对象。
输出:
{
"app": "user",
"status": "success",
"date": "20241212"
}
{
"app": "order",
"status": "fail",
"date": "20241212"
}
{
"app": "user",
"status": "fail",
"date": "20241212"
}
分组与计数:group_by() + map()
group_by(字段) 按指定字段对数组元素分组(返回嵌套数组,每组为相同字段值的元素集合);map(表达式) 对分组后的数组进行转换(如计算每组长度即计数)。
示例:按 app 和 status 分组,统计 20241212 当天各组合的数量:
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):按app和status的组合分组,结果为嵌套数组(如[[user-success元素], [order-fail元素], ...])。map(...):遍历每个分组,用.[0].app获取组内第一个元素的app(因分组后组内元素该字段相同),length为组内元素数量(计数)。
输出:
[
{
"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 内容(每行一个对象):
{"app": "user", "status": "success", "date": "20241212"}
{"app": "order", "status": "fail", "date": "20241212"}
{"app": "user", "status": "success", "date": "20241213"}
{"app": "user", "status": "fail", "date": "20241212"}
合并为数组并查看:
jq -s '.' data.jsonl # -s 将所有行合并为一个数组
输出:
[
{"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.jsonl 中 20241212 当天各 app 和 status 的数量:
jq -s '
map(select(.date == "20241212")) | # 筛选日期符合的记录
group_by([.app, .status]) | # 按[app, status]组合分组(数组形式)
map({
app: .[0].app,
status: .[0].status,
count: length
})
' data.jsonl
说明:
-s确保所有行被合并为一个数组,后续map和group_by可作用于整个数据集。group_by([.app, .status])与group_by(.app, .status)效果相同,均按两个字段组合分组。
输出:与 JSON 数组示例相同(按 app 和 status 分组的计数结果)。
进阶操作:提取、转换与条件逻辑
提取指定字段
用 {新字段名: .原字段名} 提取需要的字段,简化输出。
示例:从 data.json 中提取 app 和 date 字段:
jq '.[] | {应用: .app, 日期: .date}' data.json
输出:
{ "应用": "user", "日期": "20241212" }
{ "应用": "order", "日期": "20241212" }
...
条件转换:if-else
根据字段值进行条件转换(如将 status 转换为中文)。
示例:将 status 为 success 转为 “成功”,fail 转为 “失败”:
jq '.[] | {
app: .app,
状态: if .status == "success" then "成功" else "失败" end
}' data.json
嵌套 JSON 处理
对于嵌套结构(如 {a: {b: 1}}),用 .a.b 访问深层字段。
示例:处理嵌套 JSON:
// nested.json
[
{"app": "pay", "detail": {"code": 200, "msg": "ok"}},
{"app": "pay", "detail": {"code": 500, "msg": "error"}}
]
提取 app 和 detail.code:
jq '.[] | {app: .app, code: .detail.code}' nested.json
实用技巧与常见场景
1. 格式化输出
默认输出已格式化,若需紧凑格式(无缩进),用 -c 选项:
jq -c '.[] | select(.date == "20241212")' data.json # 紧凑输出
2. 日志分析场景
结合管道处理实时日志流(如从 tail 输出中筛选特定日志):
tail -f app.log | jq -c 'select(.level == "error")' # 实时打印错误级别日志
3. 计数去重
统计某个字段的去重值及出现次数:
jq -s '
group_by(.app) |
map({app: .[0].app, count: length})
' data.jsonl # 统计每个app的出现次数