理解 jq 时,最容易走偏的路径,是把它学成一包零碎技巧。一个命令打印某个键,一个命令取第零项,另一个命令筛数组。这样的路径在最初几分钟里很顺手,一旦表达式开始嵌套,阅读感就会迅速崩掉,因为它训练出来的是“字段提取器”视角,距离“语言”视角仍有一层。[1][2][3]
Ulrik Aabye-Hansen 在 MacSysAdmin 2025 的这场演讲之所以值得看,原因正在这里。它从很实用的壳层操作进入,往前推进时,却不断把 jq 更深一层的模型暴露出来。[1][4] 这场分享面向的是要处理 Apple Business Manager、Jamf Pro 与 Microsoft Intune API 输出的管理员,但真正能留下来的东西,比 Apple 设备管理更宽。[1][4] 当命令不再停留在 .[0] 或单字段提取时,整场演讲反复回到同一个中心:jq 由过滤器组成,过滤器接收输入、产出输出,而且可以从同一份 JSON 里发出不止一个结果。[1][2]
jqlang 官方手册把这个中心说得很直接。一个 jq 程序本身就是一个过滤器;过滤器可以接到别的过滤器后面,也可以把输出收进数组;有些过滤器会产出多个结果;在其他语言里要靠循环和迭代完成的事,在 jq 里常常只是把过滤器接起来。[2] 放在这个框架里,这场演讲一下子就顺了。本文的判断是:jq 一旦不再被读成“帮我取这个字段”,而被读成“把当前 JSON 值送进一连串变换”,它的语法压力会明显下降。[1][2][3]
图像说明:题图已由原先的标题投影帧,换成更具现场感的开发者工作台照片,避开流程图、抽象 JSON 图形和带有强烈幻灯片属性的画面。放在本文语境里,壳层、编辑器、管道和输出结果处在同一块工作表面上,这比单张标题页更能承接文章关于 jq 阅读模型的讨论。[1][2]
大约在 20:55,点号超出占位符,它承担恒等过滤器的工作
这场演讲真正的概念转折,出现在 Aabye-Hansen 把引号里的表达式称作“过滤器”,并解释 . 指向文档根部的时候。[1] 表面看,这像是在做入门语法说明;顺着 jq 自己的手册看,这其实已经把整门语言收进了一句里。官方手册说,最简单的 jq 程序就是 .,它是恒等操作:输入什么,就把什么作为输出送出去。[2] 官方教程也用最熟悉的例子说明这一点:把 API 返回的 JSON 原样漂亮地打印出来。[3]
因此,jq '.year' file.json 超出讨巧字段提取动作这一层。[1][2] 它背后先有一层更基本的事:从整份输入出发,然后把它送进字段访问表达式。这个动作一旦在脑子里站稳,后面那些看上去像标点风暴的复杂命令,就会开始显出结构,它们会逐渐脱离谜语感,显出在一个明确输入值上连续做事的结构。
这段演讲里还有一个关于壳层引号的提醒,分量也比表面大。[1] 官方手册明确写到,正确引用很重要,因为很多 jq 字符同时也是 shell 的元字符。[2] 放在实际操作里,这意味着 jq 表达式一直处在两种语言的边界上。把过滤器当作程序本身认真对待,先把引用写对,后面的命令行阅读就会稳定很多。
大约在 24:30 和 26:24,空方括号与管道把 JSON 树变成一股流
接下来最关键的一步,发生在演讲进入数组的时候。Aabye-Hansen 先用显式索引取单个 counselor,随后展示空方括号可以直接遍历整个数组。[1] 他把这叫作自己见过最简单的循环,这句玩笑正好点到了 jq 的核心技巧:数组不只是拿来取第几项的容器,它还是一个可以把值一个一个发出来的地方。
官方手册用更正式的方式写出了这件事。有些过滤器会产出多个结果,而把这样的过滤器接进第二个过滤器时,右侧过滤器会对左侧发出的每个结果分别运行一次。[2] 这句话恰好补上“JSON 树”和“jq 程序”之间那座桥。[] 超出“把所有元素拿出来”的缩写层面,它会把一个数组输入拆成按元素发出的结果流。也正因为这样,.campData.counselors[].name 和 .campData.counselors[0].name 的手感完全不同:前者已经在处理多个结果,后者仍然停在单一值上。[1][2]
26:24 左右关于管道的讲解,又从终端一侧把同一个意思补齐了。[1] Aabye-Hansen 用大家熟悉的 shell 直觉解释 |,这当然有帮助,但放回 jq 内部,管道超出好看的串联记号。[1] 它的工作,是把左侧过滤器送出的每个结果,继续喂给右侧过滤器。[2] 这一点一旦站稳,很多 jq 语法会突然变得可读。管道的作用超出把几个小命令缝起来的层面,它写下的是数据如何向前流动。
大约在 28:57,select 说明控制流仍然留在过滤模型内部
演讲里按年龄筛选 21 岁以上 counselors 的部分,是很多新手真正从“提字段”跨到“学语言”的地方。[1] Aabye-Hansen 把 select 解释得像 if-then 语句,这是一种很实用的讲法。[1] 再往里看一步,更强的理解是:jq 把控制流也留在过滤器模型内部,没有跳到另一套完全不同的语法里。
表达式左侧先把 counselors 一个一个送出来,select(.age > 21) 再把满足条件的值留下。[1][2] 因此,许多命令式读法直觉里的“先存一份临时列表,再处理它”并没有发生;结果仍是同一股流,只是范围被收窄了。这也是 jq 在语法很短的情况下仍然显得有力量的原因。筛选、投影、重塑,始终在同一种管线语言里完成。
官方教程其实已经替这个模式铺好了底:就连最基本的索引,也是在当前输入上施加过滤器。[3] 这场演讲把它推进成管理员每天会遇到的真实路径:从 API 拉回 JSON,走进关心的嵌套数组,把每个元素继续送往下游,只保留符合条件的记录,再把真正需要的字段打印出来。[1] 到这里,jq 逐渐脱离一串聪明符号的样子,更接近一门给运维与自动化工作准备的紧凑数据流语言。
大约在 53:40,map(...) 是 jq 有意识把流重新收束成结构的时候
这场分享最后一个很有价值的教学时刻,出现在它从检查单条记录转向总结输出的时候。[1] Aabye-Hansen 先按操作系统版本分组,再用 map(...) 组出一个更小的结果结构,里面带着标签和计数,中间还现场修正了一次括号位置,因为内部映射逻辑需要被括起来。[1] 这个结尾很重要,因为它展示的是和 [] 方向相反的动作。
前面,空方括号把一个数组展开成一串结果。[1][2] 到这里,map(...) 则把按组处理后的内容重新收回一个新的数组结构里。官方手册说,过滤器可以被组合,输出也可以被收进数组。[2] 这正是更深的模式。jq 的工作不只是“往里钻”,还包括判断什么时候把值展开,什么时候再把它们收束成真正要交给别的工具、别的人、或者下一个自动化步骤的摘要对象。
也正因为这样,演讲中那些看似很具体的设备管理示例,超出了窄领域演示的范围。[1][4] 依赖 API 的日常操作,往往就在这两种动作之间来回切换:先把一大块嵌套响应拆开,变成可以逐个理解的项;再把结果重新装配成更小、更适合决策的结构。jq 之所以显得优雅,恰恰因为这两种运动在语法里都保持可见。
因此,记住这支视频的最好方式,可以把它从围绕恐怖片主题的有趣终端演示里再往前推一步,读成一场很干净的初学者证明:jq 有自己一致的语法骨架。. 从当前输入起步,[] 把数组展开成多个结果,| 把每个结果继续送下去,select 收窄这股流,map(...) 再把结构收回来。按照这个方式去读,单行命令会脱离小把戏的状态,开始显出真正程序的形状。[1][2][3]
来源
- MacSysAdmin Conference,《Friday the jq’th - Json Lives》,Ulrik Aabye-Hansen 主讲的 YouTube 视频。
- jqlang,《jq 1.8 Manual》:过滤器、管道、多结果行为、引用规则、流与数组收集。
- jqlang,《Tutorial》:
.作为恒等操作、基础索引,以及围绕 JSON API 输出的壳层管线示例。 - MacSysAdmin Conference 2025 日程页,《Friday the jq’th - Json Lives》:议程说明、讲者署名,以及面向管理员的 API 场景。