命令行 JSON 处理:jq 的过滤与变换
港旧胶片
5 min read
curl 拉回来的接口响应往往是一长串嵌套 JSON,直接肉眼找字段既慢又容易看错。jq 把 JSON 当管道里的数据流来处理:读 stdin、按表达式筛选、写 stdout,可以接进 grep/sort 那套 Unix 工具链。它和用 Python 现写解析脚本的区别,在于无需为一次性查询起一个文件,一行表达式就能干活。
先会四个基本操作
jq '.' data.json # 格式化美化输出
jq '.name' data.json # 取顶层字段
jq '.users[0].email' data.json # 取数组第 0 个元素的字段
jq '.users[]' data.json # 把数组展开成逐行对象
.users[] 的 [] 是关键:它把数组"拍平"成多个独立对象,后面才好逐个处理。配合管道 | 能在一次表达式里串联多步。点号开头的路径语法支持嵌套任意深,写错一层就返回 null,这是定位"为什么取不到值"的第一线索——先单独打印父级看结构,再逐级往下。
过滤:按条件筛元素
jq '.users[] | select(.age > 18)' data.json
jq '.users[] | select(.city == "Shanghai")' data.json
select(条件) 只保留满足条件的元素。条件里 >、==、and、or、not 都能用,字符串比较区分大小写。注意 == 比较的是类型和值,数字 1 和字符串 "1" 不相等,这是常见的坑,尤其接口有时把数字写成字符串时。要忽略类型,可先 tonumber 或 tostring 归一化再比。
变换:重构成新结构
jq '[.users[] | {name, contact: .email}]' data.json
这段把每个用户对象映射成只含 name 和 contact 的新对象,[] 外层再包回数组。用 map() 写法等价且更直白:jq '.users | map({name, contact: .email})'。变换后的字段名可以任取,比如把 email 改名成 contact。jq 的所有变换都是不可变的,原输入不会被改,输出是新生成的结构,这让你可以放心地一层层试表达式而不怕弄脏源数据。
字符串与拼接
jq -r '.users[] | "\(.name) 的联系方式是 \(.email)"' data.json
-r 输出原始字符串而非带引号的 JSON 字符串,适合直接拼成日志或行文本。插值用 \(表达式) 语法,跟很多模板语言一致。需要换行或制表时,在字符串里直接写 \n、\t 即可,jq 会正确转义输出。
从文件到管道
jq 既能读文件也能吃管道,后者在接口调试里更常用:
curl -s 你的接口地址/v1/orders \
| jq '.items[] | select(.status == "pending") | .id'
这里 example.com 仅作占位,实际换成你的接口。遇到超大 JSON,jq 是流式逐块解析,不会一次性把整文件读进内存峰值拉爆,处理几 GB 的转储文件也能动。配合 head 可以先取前几条快速验证表达式对不对,再去掉 head 跑全量。
边界与排错
JSON 不合法时 jq 直接报 parse error,先用 jq empty 做语法校验最快,它只验证不输出,速度也快。数字精度上,jq 用双精度浮点,超出 2^53 的大整数可能丢精度,处理雪花 ID 一类大整数要警惕,必要时用字符串保留原值。null 字段取子属性会报错,可用 // 默认值兜底:.user.email // "unknown",当左侧为 null 或 false 时取右侧。掌握 //、?、select 这三个,日常九成的清洗需求都能覆盖。
1 view
0 likes
0 comments