jq — JSON 查询与转换
选择性阅读规则
从以下内容开始:
references/senior-master-standard.mdreferences/usage-routing.mdreferences/quality-checklist.md
然后仅加载与用户实际任务匹配的继承文档、脚本、资产或示例。
概述
jq 是用于查询和重塑 JSON 的标准 CLI 工具。此技能涵盖实用、专家级的用法:过滤深度嵌套的数据、转换结构、聚合值以及将 jq 集成到 shell 管道中。每个示例均可直接复制粘贴到实际工作流程中。
何时使用此技能
- 用于解析来自 API、CLI 工具(AWS、GitHub、kubectl、docker)或日志文件的 JSON 输出时
- 用于转换 JSON 结构(重命名键、展平数组、分组记录)时
- 当用户需要在 bash 脚本或单行命令中使用
jq时 - 当需要解释复杂的
jq表达式的作用时
工作原理
jq 接受一个过滤器表达式并将其应用于 JSON 输入。过滤器通过管道(|)组合,jq 原生支持数组、对象、字符串、数字、布尔值和 null。
基本选择
# 提取字段
echo '{"name":"alice","age":30}' | jq '.name'
# "alice"
# 嵌套访问
echo '{"user":{"email":"[email protected]"}}' | jq '.user.email'
# 数组索引
echo '[10, 20, 30]' | jq '.[1]'
# 20
# 数组切片
echo '[1,2,3,4,5]' | jq '.[2:4]'
# [3, 4]
# 所有数组元素
echo '[{"id":1},{"id":2}]' | jq '.[]'
使用 select 过滤
# 仅保留匹配的元素
echo '[{"role":"admin"},{"role":"user"},{"role":"admin"}]' \
| jq '[.[] | select(.role == "admin")]'
# 数值比较
curl -s https://api.github.com/repos/owner/repo/issues \
| jq '[.[] | select(.comments > 5)]'
# 测试字段存在且非 null
jq '[.[] | select(.email != null)]'
# 组合条件
jq '[.[] | select(.active == true and .score >= 80)]'
映射与转换
# 从每个数组元素提取字段
echo '[{"name":"alice","age":30},{"name":"bob","age":25}]' \
| jq '[.[] | .name]'
# ["alice", "bob"]
# 简写:map()
jq 'map(.name)'
# 为每个元素构建新对象
jq '[.[] | {user: .name, years: .age}]'
# 添加计算字段
jq '[.[] | . + {senior: (.age > 28)}]'
# 重命名键
jq '[.[] | {username: .name, email_address: .email}]'
聚合与 reduce
# 对所有值求和
echo '[1, 2, 3, 4, 5]' | jq 'add'
# 15
# 对对象间的字段求和
jq '[.[].price] | add'
# 计数元素
jq 'length'
# 最大值 / 最小值
jq 'max_by(.score)'
jq 'min_by(.created_at)'
# reduce:自定义累加器
echo '[1,2,3,4,5]' | jq 'reduce .[] as $x (0; . + $x)'
# 15
# 按字段分组
jq 'group_by(.department)'
# 每组计数
jq 'group_by(.status) | map({status: .[0].status, count: length})'
字符串插值与格式化
# 字符串插值
jq -r '.[] | "\(.name) 年龄 \(.age) 岁"'
# 格式化为 CSV(无表头)
jq -r '.[] | [.name, .age, .email] | @csv'
# 格式化为 TSV
jq -r '.[] | [.name, .score] | @tsv'
# 对值进行 URL 编码
jq -r '.query | @uri'
# Base64 编码
jq -r '.data | @base64'
处理键与路径
# 列出所有顶层键
jq 'keys'
# 检查键是否存在
jq 'has("email")'
# 删除一个键
jq 'del(.password)'
# 从每个元素删除嵌套键
jq '[.[] | del(.internal_id, .raw_payload)]'
# 递归下降:在树中任意位置查找某个键的所有值
jq '.. | .id? // empty'
# 获取所有叶节点路径
jq '[paths(scalars)]'
条件与错误处理
# if-then-else
jq 'if .score >= 90 then "A" elif .score >= 80 then "B" else "C" end'
# 替代运算符:如果为 null 或 false 则使用后备值
jq '.nickname // .name'
# try-catch:跳过错误而非停止
jq '[.[] | try .nested.value catch null]'
# 使用 // empty 抑制 null 输出
jq '.[] | .optional_field // empty'
实际 Shell 集成
# 从文件读取
jq '.users' data.json
# 紧凑输出(无空白)用于进一步管道处理
jq -c '.[]' records.json | while IFS= read -r record; do
echo "正在处理:$record"
done
# 将 shell 变量传递给 jq
STATUS="active"
jq --arg s "$STATUS" '[.[] | select(.status == $s)]'
# 传递数字
jq --argjson threshold 42 '[.[] | select(.value > $threshold)]'
# 将多行 JSON 吞入数组
jq -s '.' records.ndjson
# 多个文件:将所有文件吞入一个数组
jq -s 'add' file1.json file2.json
# 来自命令的空安全管道
kubectl get pods -o json | jq '.items[] | {name: .metadata.name, status: .status.phase}'
# GitHub CLI:提取 PR 编号
gh pr list --json number,title | jq -r '.[] | "\(.number)\t\(.title)"'
# AWS CLI:列出正在运行的实例 ID
aws ec2 describe-instances \
| jq -r '.Reservations[].Instances[] | select(.State.Name=="running") | .InstanceId'
# Docker:显示容器名称和镜像
docker inspect $(docker ps -q) | jq -r '.[] | "\(.Name)\t\(.Config.Image)"'
高级模式
# 将对象数组转置为对象数组
# 输入:{"names":["a","b"],"scores":[10,20]}
jq '[.names, .scores] | transpose | map({name: .[0], score: .[1]})'
# 展平一层
jq 'flatten(1)'
# 按字段去重
jq 'unique_by(.email)'
# 排序、去重并重新索引
jq '[.[] | .name] | unique | sort'
# Walk:递归地对每个节点应用转换
jq 'walk(if type == "string" then ascii_downcase else . end)'
# env:在 jq 内部读取环境变量
export API_KEY=secret
jq -n 'env.API_KEY'
最佳实践
- 当将
jq结果传递给 shell 变量或其他命令时,始终使用-r(原始输出)以去除 JSON 字符串引号 - 使用
--arg/--argjson安全地注入 shell 变量 — 切勿将 shell 变量直接插值到过滤器字符串中 - 推荐使用
map(f)替代[.[] | f]以提高可读性 - 对于换行分隔的 JSON 管道使用
-c(紧凑);对于人类可读的调试场景则不使用 - 在将过滤器嵌入脚本之前,先使用
jq -n和字面输入交互式测试过滤器 - 使用
empty丢弃不需要的元素,而不是过滤为null
安全注意事项
jq在设计中是只读的 — 它不能写入文件或执行命令- 避免将不受信任的 JSON 字段值直接嵌入到 shell 命令中;始终引用或使用
--arg
常见陷阱
-
问题:
jq输出null而不是期望的值 解决方案: 检查键名是否拼写错误;使用keys检查实际字段名称。记住 JSON 区分大小写。 -
问题: 输出中数字被引用为字符串 解决方案: 在注入数值时使用
--argjson而不是--arg。 -
问题: 过滤器在终端中有效,但在脚本中失败 解决方案: 确保过滤器字符串在 shell 中使用单引号以防止变量扩展。示例:
jq '.field'而不是jq ".field"。 -
问题: 空数组上
add返回null解决方案: 使用add // 0或add // ""提供后备默认值。 -
问题: 流式处理大文件很慢 解决方案: 对于非常大的文件,使用
jq --stream或切换到jstream/gron。
相关技能
@bash-pro— 将 jq 调用包装在健壮的 shell 脚本中@bash-linux— 通用 shell 管道模式@github-automation— 配合 GitHub CLI JSON 输出使用 jq
限制
- 仅当任务明确匹配上述范围时使用此技能。
- 请勿将输出视为环境特定验证、测试或专家审查的替代。
- 如果缺少必需的输入、权限、安全边界或成功标准,请停止并询问澄清。