jq:命令行里处理 JSON 的瑞士军刀
调 API、看 Docker inspect、处理配置文件,输出的都是 JSON。用眼睛看又长又乱,用 jq 一下就清爽了。
一、安装与第一个命令
sudo apt install -y jq # Debian/Ubuntu
brew install jq # macOS
# 格式化(最常用)
curl -s https://api.example.com/user | jq .不加 jq 时是一坨,加了之后自动缩进 + 语法高亮。这是 80% 场景的用法。
二、取字段
# 取顶层字段
jq '.name'
# 嵌套
jq '.data.user.email'
# 多个字段(构造数组)
jq '[.name, .age]'
# 构造对象
jq '{n: .name, a: .age}'
# 取不到时给默认值(避免 null)
jq '.nickname // "匿名"'原始输出(去掉引号)
# -r 输出原始字符串,方便脚本里用
jq -r '.token'
# 常见组合:拿到值直接用
TOKEN=$(curl -s ... | jq -r '.access_token')
curl -H "Authorization: Bearer $TOKEN" ...脚本里一定要加
-r,否则拿到的是带引号的字符串,拼进命令会出错。
三、数组处理
# 数组长度
jq '.items | length'
# 遍历(.[] 把数组拆成流)
jq '.items[]'
# 取每个元素的某字段
jq -r '.items[].name'
# 索引 / 切片
jq '.items[0]'
jq '.items[-1]' # 最后一个
jq '.items[2:5]' # 切片
# 重新包成数组(注意括号)
jq '[.items[].name]' # → ["a","b"]
jq '.items[].name' # → "a" "b"(两个独立输出)四、过滤与条件
# select:筛选
jq '.items[] | select(.status == "active")'
jq '.items[] | select(.age > 18 and .vip)'
# 判断字段是否存在
jq '.items[] | select(has("email"))'
# 正则匹配(test)
jq '.items[] | select(.name | test("^baize"))'
# 包含子串
jq '.items[] | select(.title | contains("Nginx"))'
# map:对数组每个元素做变换
jq '.items | map({id, name})'五、管道与函数
# 管道串联
jq '.data | .users | map(.name) | sort'
# 常用函数
jq 'keys' # 对象的所有键
jq 'keys[]' # 键逐个输出
jq 'to_entries' # 转成 [{key,value}]
jq 'map_values(. + 1)' # 对所有值做运算
jq 'unique' # 去重
jq 'group_by(.category)' # 分组
jq 'sort_by(.date) | reverse' # 排序
jq 'add' # 求和
jq 'join(",")' # 拼接
# 字符串处理
jq -r '.name | ascii_downcase'
jq -r '.path | ltrimstr("/var/www")'
jq -r '.url | sub("^https?"; "")'六、实战:处理 API 响应
# Cloudflare API:列出所有 DNS 记录的名字和值
curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$ZONE/dns_records" \
-H "Authorization: Bearer $TOKEN" | jq -r '.result[] | "\(.type)\t\(.name)\t\(.content)"'
# Docker:看容器端口映射
docker inspect web | jq -r '.[0].NetworkSettings.Ports'
# 找所有运行中的容器名
docker ps --format json | jq -r '.Names'
# 从 JSON 数组生成 shell 循环
jq -r '.items[] | .id' data.json | while read -r id; do
echo "处理 $id"
done七、在脚本里判断与取值
#!/usr/bin/env bash
resp=$(curl -s https://api.example.com/status)
# 用 --exit-status 判断:输出 false/null 时退出码非 0
if echo "$resp" | jq -e '.ok == true' > /dev/null; then
echo "服务正常"
else
echo "异常:$(echo "$resp" | jq -r '.message // "未知"')"
exit 1
fi
# 安全地取值(字段不存在时给默认)
name=$(echo "$resp" | jq -r '.data.name // empty')
[ -z "$name" ] && name="unknown"八、修改 JSON(写回)
# 改一个字段(输出新内容,不原地改)
jq '.version = "1.2.0"' package.json > tmp && mv tmp package.json
# 删除字段
jq 'del(.debug)' config.json
# 追加数组元素
jq '.tags += ["new"]' data.json
# 批量改
jq '.items |= map(.active = true)' data.jsonjq 不支持原地修改。要么重定向到临时文件,要么用
sponge(moreutils包):jq '...' f | sponge f。
九、替代方案对比
| 工具 | 优点 | 缺点 |
|---|---|---|
| jq | 专注 JSON,语法紧凑 | 语法有点怪 |
| python -m json.tool | 到处都有 | 只能格式化,不能查询 |
| python + json 模块 | 灵活 | 要写脚本 |
| fx / jless | 交互式浏览,体验好 | 需要额外安装 |
# 系统没 jq 时的应急方案
curl -s ... | python3 -c "import json,sys; d=json.load(sys.stdin); print(d['data']['name'])"
# 只看格式化
curl -s ... | python3 -m json.tool十、记住这几条就够
jq . # 格式化
jq -r '.a.b' # 取值(脚本用)
jq -r '.arr[].field' # 取数组每项的字段
jq '.arr[] | select(.x==1)' # 筛选
jq -e '.ok' # 判断(配合退出码)
jq 'keys' # 看结构装个 jq,以后凡是遇到 JSON,第一反应就是管道给它。
