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 | # CentOS 7 及以上(需启用 EPEL 源) |
(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 | jq --version |
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 | [ |
筛选 date 为 20241212 的记录:
1 | jq '.[] | select(.date == "20241212")' data.json |
.[]:遍历数组中的每个元素(将数组 “展开” 为单个对象)。select(.date == "20241212"):保留date字段等于20241212的对象。
输出:
1 | { |
分组与计数:group_by() + map()
group_by(字段) 按指定字段对数组元素分组(返回嵌套数组,每组为相同字段值的元素集合);map(表达式) 对分组后的数组进行转换(如计算每组长度即计数)。
示例:按 app 和 status 分组,统计 20241212 当天各组合的数量:
1 | jq ' |
解析:
map(select(...)):对数组中每个元素应用筛选,返回筛选后的新数组(避免用.[]展开,否则后续无法分组)。group_by(.app, .status):按app和status的组合分组,结果为嵌套数组(如[[user-success元素], [order-fail元素], ...])。map(...):遍历每个分组,用.[0].app获取组内第一个元素的app(因分组后组内元素该字段相同),length为组内元素数量(计数)。
输出:
1 | [ |
处理 JSON Lines:每行一个 JSON 对象
日志文件常采用 “JSON Lines” 格式(.jsonl),即每行一个独立的 JSON 对象(而非整个文件为一个数组)。此时需先用 jq 的 -s 选项将所有行合并为一个数组,再进行聚合操作。
-s 选项:合并多行为数组
-s(--slurp)选项会将所有输入行读入一个大数组(每行一个元素),从而支持跨行进分组、聚合。
示例:data.jsonl 内容(每行一个对象):
1 | {"app": "user", "status": "success", "date": "20241212"} |
合并为数组并查看:
1 | jq -s '.' data.jsonl # -s 将所有行合并为一个数组 |
输出:
1 | [ |
聚合操作:分组计数
对 JSON Lines 文件进行分组计数,步骤与 JSON 数组类似(先合并为数组,再筛选、分组)。
示例:统计 data.jsonl 中 20241212 当天各 app 和 status 的数量:
1 | jq -s ' |
说明:
-s确保所有行被合并为一个数组,后续map和group_by可作用于整个数据集。group_by([.app, .status])与group_by(.app, .status)效果相同,均按两个字段组合分组。
输出:与 JSON 数组示例相同(按 app 和 status 分组的计数结果)。
进阶操作:提取、转换与条件逻辑
提取指定字段
用 {新字段名: .原字段名} 提取需要的字段,简化输出。
示例:从 data.json 中提取 app 和 date 字段:
1 | jq '.[] | {应用: .app, 日期: .date}' data.json |
输出:
1 | { "应用": "user", "日期": "20241212" } |
条件转换:if-else
根据字段值进行条件转换(如将 status 转换为中文)。
示例:将 status 为 success 转为 “成功”,fail 转为 “失败”:
1 | jq '.[] | { |
嵌套 JSON 处理
对于嵌套结构(如 {a: {b: 1}}),用 .a.b 访问深层字段。
示例:处理嵌套 JSON:
1 | // nested.json |
提取 app 和 detail.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 | jq -s ' |
v1.3.10