jq 入门:从格式化到复杂过滤

jq 是命令行里处理 JSON 的常用工具,用管道把「取值、过滤、分组、排序、重塑」串成一条表达式, 比手写脚本省事。本文用一份固定的 store / book 示例数据,把 .、[]、| 三条基本功与常用函数讲清楚,示例都能在站内 jq 在线运行工具 里复现(官方 jq 1.8,浏览器内 WASM 执行,无需安装)。

一、先准备一份示例数据

下面这份数据和站内工作台内置的「填入示例」一致,本文所有表达式都可以直接对 它 执行, 结果与文中一致:

{
  "store": {
    "book": [
      { "category": "reference", "title": "Sayings", "author": "Nigel Rees", "price": 8.95 },
      { "category": "fiction", "title": "Sword of Honour", "author": "Evelyn Waugh", "price": 12.99 },
      { "category": "fiction", "title": "Moby Dick", "author": "Herman Melville", "price": 8.99 }
    ],
    "bicycle": { "color": "red", "price": 19.95 }
  }
}

二、jq 的三条基本功

每条 jq 表达式描述的都是「对当前输入做什么」,把下面三种动作串起来,就能写出大部分查询:

  • 取字段:.key。. 代表「当前输入」本身, .store 取 store 字段, 再 .book 继续下钻;字段名带特殊符号时用 .["weird-key"]。
  • 展开数组:[]。.store.book 是整个数组(一个值); .store.book[] 会把数组拆开,把三个元素依次输出(三个值)。
  • 管道:|。 把左边的输出当作右边的输入:.store.book | length 即「先取 book,再求长度」。表达式从左到右读,就是数据的流动方向。

用方括号把一串管道包起来,可把「多行输出」重新收集成一个数组:[.store.book[] | .title] 得到 ["Sayings","Sword of Honour","Moby Dick"],而不加方括号时是三个独立输出。

三、jq 的常用写法

下表覆盖入门最常用的一批写法,均可对示例数据直接运行:

写法含义示例
.key取对象字段.store.bicycle.color
[i]数组下标(负数从末尾数).store.book[0].title
[]展开数组为多个输出.store.book[]
|管道,左值作为右值输入.store.book | length
map(f)对数组每项应用 f,返回数组map(.price)
select(f)保留满足条件的项(只过滤,不投影)select(.price < 10)
[ ... ]把多行输出收集成数组[.store.book[] | .title]

更进阶的内建函数如 group_bysort_byaddtest// 见下文实战与速查表。

四、实战:对示例数据逐条运行

下表每一行都可以在上面的示例数据上复现(结果为实际输出值):

目标表达式结果
取单个字段.store.bicycle.color"red"
第一本书的标题.store.book[0].title"Sayings"
最后一本书.store.book[-1].title"Moby Dick"
展开数组取所有标题.store.book[] | .title三行输出:"Sayings"、"Sword of Honour"、"Moby Dick"
所有价格数组.store.book | map(.price)[8.95, 12.99, 8.99]
书的数量.store.book | length3
价格低于 10 的书(完整对象)[.store.book[] | select(.price < 10)]两条 book 对象:Sayings、Moby Dick
价格低于 10 的标题[.store.book[] | select(.price < 10) | .title]["Sayings", "Moby Dick"]
按价格升序取标题.store.book | sort_by(.price) | .[].title"Sayings"、"Moby Dick"、"Sword of Honour"
平均价格.store.book | map(.price) | add / length10.31
store 的键(已排序).store | keys["bicycle", "book"]
字段缺失用默认值.store.car // {color: "unknown"}{color: "unknown"}

五、常见错误与排查

  • select 只过滤、不投影。select(.price < 10) 输出的是整个元素;只要标题需再接一段管道: select(.price < 10) | .title。
  • 想要数组却忘了方括号。.store.book[] | .title 是三个独立输出(三行裸串);套上外层方括号 [.store.book[] | .title] 才得到一个数组。
  • 把 JSONPath 的 $ 或 @ 带进 jq。 jq 里的「当前输入」是 ., 不是 $ 或 @;过滤要写 select(.price < 10), 容器与字段都点开头,例如 .store.book。
  • 对字符串做算术。 对非数字值做 +- 或 tonumber 会触发运行时错误, 结果区会原样显示 jq 报错并归类为「jq 运行时错误」。
  • 误以为 keys 保持原顺序。keys 返回的是排序后的键名(示例里是 ["bicycle","book"]), 不是数据里的书写顺序;需要原顺序时可结合 to_entries。

排查建议:先用 . 看整体结构,再用 keys 或 .. 确认层级,然后逐步加管道。 对 null 取字段在 jq 1.8 中不报错(如 {} | .a.b 得 null), 取不到数据时优先怀疑层级,而不是以为表达式崩了。

六、速查表

工作台内置的速查卡按「基础 / 遍历与映射 / 过滤与分组 / 对象与默认值」四组给出可运行示例,点击即填入表达式,可对照下表速记:

分类表达式说明
基础.store.book[-1].title负数取最后一个
遍历映射.store.book | map(.price)映射为字段数组
重塑结构[.store.book[] | {title, price}]重塑为新对象数组
过滤[.store.book[] | select(.price < 10)]条件过滤成数组
分组.store.book | group_by(.category)按字段分组(先排序)
排序.store.book | sort_by(.price)按字段升序排序
正则匹配[.store.book[] | select(.author | test("Waugh"))]按正则筛选元素
拼接字符串.store.book | map(.title) | join(", ")数组拼接为字符串
默认值.store.car // {color: "unknown"}空值兜底(// 操作符)

现在就试一条 jq 表达式

粘贴 JSON 即可用官方 jq 1.8 实时过滤、映射、分组与排序,全部在浏览器本地完成,数据不会上传。

常见问题

jq 和 JSONPath 有什么区别?该先学哪个?

JSONPath 只做「路径定位」,语法接近 XPath,一行表达式选出节点,上手快;jq 是完整的数据处理语言,有管道、map / select / group_by、排序和对象重塑,能力更强但语法更多。建议先用 JSONPath 快速取值,需要过滤、分组、改写结构时再切 jq;本站两个引擎在同一页面切换,JSON 数据保持不变。

.store.book[] 和 .store.book 有什么区别?

`.store.book` 得到的是「book 这个数组」本身(一个值);在后面加 [] 变成 `.store.book[]` 会「展开」数组,把三个元素依次输出(三个值)。想看数组长度用 `.store.book | length`,想逐个处理元素用 `.store.book[]`,这就是 `[]` 的关键作用。

select 过滤后为什么输出的是整个元素,不是某个字段?

select 只负责过滤、不做投影:`[.store.book[] | select(.price < 10)]` 输出的是完整的 book 对象数组。只要标题就再接一段管道 `| .title`,即 `[.store.book[] | select(.price < 10) | .title]`。这是 jq 管道从左到右阅读最直观的体现。

为什么 select(.price < 10) 里不能用 @?

`@` 是 JSONPath 里代表当前节点的写法,jq 不用 @:jq 中「当前输入」就是 `.`,所以过滤写成 `select(.price < 10)`。把 JSONPath 表达式直接粘到 jq 引擎里最常见的报错,就是误用了 $ 或 @。

在线运行 jq 会上传我的数据吗?第一次为什么要等一下?

不会上传。jq 引擎跑在浏览器本地的 Web Worker 中,站点没有后端服务,可打开开发者工具 Network 面板自证。第一次执行 jq 查询时会按需下载并编译约 0.9MB 的 WASM 引擎(官方 jq 1.8.2,仅一次,之后走缓存),所以首查会稍慢,之后即时执行。

相关工具