jq:命令行里处理 JSON 的瑞士军刀

调 API、看 Docker inspect、处理配置文件,输出的都是 JSON。用眼睛看又长又乱,用 jq 一下就清爽了。

一、安装与第一个命令

bash
sudo apt install -y jq      # Debian/Ubuntu
brew install jq             # macOS

# 格式化(最常用)
curl -s https://api.example.com/user | jq .

不加 jq 时是一坨,加了之后自动缩进 + 语法高亮。这是 80% 场景的用法。

二、取字段

bash
# 取顶层字段
jq '.name'

# 嵌套
jq '.data.user.email'

# 多个字段(构造数组)
jq '[.name, .age]'

# 构造对象
jq '{n: .name, a: .age}'

# 取不到时给默认值(避免 null)
jq '.nickname // "匿名"'

原始输出(去掉引号)

bash
# -r 输出原始字符串,方便脚本里用
jq -r '.token'

# 常见组合:拿到值直接用
TOKEN=$(curl -s ... | jq -r '.access_token')
curl -H "Authorization: Bearer $TOKEN" ...

脚本里一定要加 -r,否则拿到的是带引号的字符串,拼进命令会出错。

三、数组处理

bash
# 数组长度
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"(两个独立输出)

四、过滤与条件

bash
# 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})'

五、管道与函数

bash
# 管道串联
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 响应

bash
# 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

七、在脚本里判断与取值

bash
#!/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(写回)

bash
# 改一个字段(输出新内容,不原地改)
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.json

jq 不支持原地修改。要么重定向到临时文件,要么用 spongemoreutils 包):jq '...' f | sponge f

九、替代方案对比

工具优点缺点
jq专注 JSON,语法紧凑语法有点怪
python -m json.tool到处都有只能格式化,不能查询
python + json 模块灵活要写脚本
fx / jless交互式浏览,体验好需要额外安装
bash
# 系统没 jq 时的应急方案
curl -s ... | python3 -c "import json,sys; d=json.load(sys.stdin); print(d['data']['name'])"
# 只看格式化
curl -s ... | python3 -m json.tool

十、记住这几条就够

bash
jq .                          # 格式化
jq -r '.a.b'                 # 取值(脚本用)
jq -r '.arr[].field'         # 取数组每项的字段
jq '.arr[] | select(.x==1)'  # 筛选
jq -e '.ok'                  # 判断(配合退出码)
jq 'keys'                    # 看结构

装个 jq,以后凡是遇到 JSON,第一反应就是管道给它。