0Pricing
DevOps Bootcamp · 课时

使用 jq 管道筛选和选择 JSON

使用 jq 选择器、管道和 select 过滤器浏览嵌套对象与数组。

使用 jq 管道筛选和选择 JSON 是 CoddyKit 上的免费 DevOps Bootcamp 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 DevOps Bootcamp 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 DevOps Bootcamp 课程共包含 4 节课。

什么是 jq,以及为什么要使用它

jq 是一款轻量而强大的命令行工具,用于解析、筛选和转换 JSON 数据。它相当于 JSON 世界中的 sed:将 JSON 通过管道传给它,就能获得结构化输出。

  • 大多数 Linux 发行版预装了它,也可以通过 apt install jq / brew install jq 安装
  • 可以与 curl、cat 及其他工具在 shell 流水线中无缝配合
  • 支持筛选、映射、归约和格式转换

基本调用方式是:jq '<filter>' file.json,或者通过管道调用:cat file.json | jq '<filter>'。筛选器 .(点号)表示恒等操作,即以易读格式打印整个文档。

# Pretty-print a JSON file
jq '.' data.json

# Or pipe from curl
curl -s https://api.github.com/users/torvalds | jq '.'

使用点号表示法选择对象字段

要访问 JSON 对象中的字段,请使用点号表示法:.fieldName。您可以串联选择器来访问嵌套对象。

  • .name — 顶层字段
  • .address.city — 嵌套字段
  • ."field-with-dash" — 包含特殊字符的字段需要使用引号

如果字段不存在,jq 会返回 null,而不是报错。这样,在处理可选字段时,无需额外进行空值检查,便可安全地在脚本中使用。

# Given: {"name":"Alice","address":{"city":"Berlin","zip":"10115"}}
echo '{"name":"Alice","address":{"city":"Berlin","zip":"10115"}}' | jq '.name'
# Output: "Alice"

echo '{"name":"Alice","address":{"city":"Berlin","zip":"10115"}}' | jq '.address.city'
# Output: "Berlin"

访问数组元素并进行迭代

访问 JSON 数组时要使用方括号表示法。jq 使用从零开始的索引。

  • .items[0] — 第一个元素
  • .items[-1] — 最后一个元素
  • .items[1:3] — 切片(从索引 1 开始,但不包含索引 3)
  • .items[] — 展开数组:将每个元素分别作为独立值输出(这就是迭代器)

迭代器 [] 是 jq 流水线的基础,它可以让您分别对每个元素应用后续筛选器。

# Given an array of users
echo '[{"name":"Alice"},{"name":"Bob"},{"name":"Carol"}]' | jq '.[0]'
# Output: {"name":"Alice"}

# Iterate all elements and extract .name from each
echo '[{"name":"Alice"},{"name":"Bob"},{"name":"Carol"}]' | jq '.[].name'
# Output:
# "Alice"
# "Bob"
# "Carol"

使用管道运算符构建 jq 流水线

与 shell 管道 | 类似,jq 也有自己的内部管道运算符。它会将一个筛选器的输出传递给下一个筛选器作为输入。

  • jq '.users[] | .name' — 迭代用户,然后提取每个用户的姓名
  • jq '.data | .items[] | .id' — 导航到 data,展开 items,然后提取 id

jq 表达式中的管道可以分步构建复杂转换。每个阶段都会接收前一阶段生成的所有内容,包括迭代器产生的多个值。

关键要点:当迭代器产生 N 个值时,每个下游筛选器都会运行 N 次,每个值运行一次。

# Nested pipeline: navigate -> iterate -> extract
echo '{"users":[{"name":"Alice","age":30},{"name":"Bob","age":25}]}' \
  | jq '.users[] | .name'
# Output:
# "Alice"
# "Bob"

# Chain more stages
echo '{"users":[{"name":"Alice","age":30},{"name":"Bob","age":25}]}' \
  | jq '.users[] | .age'
# Output:
# 30
# 25

使用 select() 进行筛选

select(condition) 筛选器仅在条件为真时传递值,否则不产生输出。它相当于 jq 版本的 SQL 中的 grep 或 WHERE。

  • select(.age > 18) — 保留 age 大于 18 的对象
  • select(.status == "active") — 相等性检查
  • select(.name | startswith("A")) — 嵌套字符串测试

将 select 与迭代器结合即可筛选数组:.items[] | select(.active) 只输出 .active 为真值的元素。

# Filter array elements by a condition
echo '[{"name":"Alice","age":30},{"name":"Bob","age":17},{"name":"Carol","age":25}]' \
  | jq '.[] | select(.age >= 18) | .name'
# Output:
# "Alice"
# "Carol"

# Filter by string equality
echo '[{"name":"Alice","role":"admin"},{"name":"Bob","role":"user"}]' \
  | jq '.[] | select(.role == "admin") | .name'
# Output: "Alice"

使用 {} 和 [] 重建对象与数组

jq 可以通过使用 {} 构造新对象、使用 [] 构造新数组来重塑数据。

  • {name: .name, city: .address.city} — 选择字段并重命名,将它们放入新对象
  • [.items[] | .id] — 将迭代得到的值重新收集到数组中
  • 简写形式:{name, age} 等价于 {name: .name, age: .age}

将流水线包裹在 [...] 中称为数组构造。当您希望输出 JSON 数组,而不是值流时,这是必不可少的。

# Reshape: keep only selected fields
echo '[{"id":1,"name":"Alice","password":"secret"},{"id":2,"name":"Bob","password":"secret"}]' \
  | jq '[.[] | {id, name}]'
# Output:
# [
#   {"id": 1, "name": "Alice"},
#   {"id": 2, "name": "Bob"}
# ]

# Collect filtered names into an array
echo '[{"name":"Alice","active":true},{"name":"Bob","active":false}]' \
  | jq '[.[] | select(.active) | .name]'
# Output: ["Alice"]

处理嵌套数组与递归下降

现实世界中的 JSON 通常嵌套很深。jq 提供了两个用于深层导航的工具:

  • .a.b.c — 结构已知时使用的显式路径
  • .. | .fieldName? — 递归下降:遍历树中的每个节点,并输出键存在处的值

?(尝试)运算符会抑制字段在某个节点不存在时产生的错误;在异构树上使用递归下降时,这一点至关重要。

对于大型文档,请谨慎使用递归下降——它会访问每个节点,可能速度较慢。结构可预测时,应优先使用显式路径。

# Explicit deep path
echo '{"a":{"b":{"c":42}}}' | jq '.a.b.c'
# Output: 42

# Recursive descent: find all "id" values anywhere in the tree
echo '{"users":[{"id":1,"profile":{"id":99}},{"id":2}]}' \
  | jq '.. | .id?'
# Output:
# 1
# 99
# 2

实用示例:解析 curl API 响应

jq 最常见的用途之一,是解析通过 curl 获取的 REST API 响应。将 curl -s(静默模式)与 jq 流水线结合,可以干净、便于编写脚本地提取数据。

  • 提取单个值:curl -s URL | jq '.field'
  • 构建汇总表:迭代数组,并使用所需字段重建对象
  • 使用 -r(原始输出)去除字符串值外层的引号——将其赋给 shell 变量时必不可少

提示:当 jq 输出将用作 shell 变量或传递给其他命令时,请始终添加 -r。

#!/usr/bin/env bash
# Fetch GitHub repo info and extract specific fields
REPO="torvalds/linux"
RESPONSE=$(curl -s "https://api.github.com/repos/${REPO}")

# Extract fields
STARS=$(echo "$RESPONSE" | jq -r '.stargazers_count')
LANG=$(echo  "$RESPONSE" | jq -r '.language')
DESC=$(echo  "$RESPONSE" | jq -r '.description')

echo "Stars : $STARS"
echo "Lang  : $LANG"
echo "Desc  : $DESC"

使用 map() 和 map_values()

jq 提供了两个便于转换集合的高阶函数:

  • map(f) — 对数组中的每个元素应用筛选器 f,并返回新数组。等价于 [.[] | f]。
  • map_values(f) — 对对象或数组中的每个值应用 f,同时保留键或索引。

与手动将流水线包裹在 [] 中相比,这些函数更易读,也是 jq 中处理应保持为数组的转换时所采用的惯用写法。

# map: extract a field from each element
echo '[{"name":"Alice","score":95},{"name":"Bob","score":80}]' \
  | jq 'map(.name)'
# Output: ["Alice", "Bob"]

# map with select: filter + transform in one step
echo '[{"name":"Alice","score":95},{"name":"Bob","score":60}]' \
  | jq 'map(select(.score >= 70) | .name)'
# Output: ["Alice"]

# map_values: multiply every value in an object by 2
echo '{"a":1,"b":2,"c":3}' | jq 'map_values(. * 2)'
# Output: {"a":2,"b":4,"c":6}

使用 // 处理可选字段和默认值

来自外部来源的 JSON 数据通常不一致——字段可能缺失或为 null。jq 提供替代运算符 //(双斜杠)来指定默认值。

  • .nickname // "anonymous" — 如果 .nickname 不是 null 或 false,则使用它;否则使用 "anonymous"
  • .count // 0 — 数值默认值
  • 与 select 结合:select((.status // "inactive") == "active")

这比 shell 中对应的 ${VAR:-default} 简洁得多,并且可以在更长的流水线中顺畅组合。

# Provide defaults for missing/null fields
echo '[{"name":"Alice","role":"admin"},{"name":"Bob"}]' \
  | jq '[.[] | {name, role: (.role // "user")}]'
# Output:
# [
#   {"name": "Alice", "role": "admin"},
#   {"name": "Bob",   "role": "user"}
# ]

# Numeric default
echo '{"items":[1,2,3]}' | jq '.total // 0'
# Output: 0

实用脚本:JSON 日志解析器

结构化 JSON 日志记录是现代系统的标准做法。下面是一个贴近实际的脚本:它读取按换行分隔的 JSON 日志文件,筛选出错误条目,并格式化为易于人类阅读的摘要。

使用的关键模式:

  • -c(紧凑输出) — 每行输出一个 JSON 对象,适合传递给 shell 循环
  • --arg name value — 将 shell 变量作为 jq 字符串参数注入
  • 使用 select 按日志级别筛选
  • 使用 -r 输出适合 echo 使用的原始字符串
#!/usr/bin/env bash
# Parse newline-delimited JSON logs and report ERRORs
# Each log line: {"level":"ERROR","msg":"...","ts":"2024-01-15T10:23:00Z","svc":"auth"}

LOG_FILE="/var/log/app/app.log"
LEVEL="ERROR"

echo "=== $LEVEL entries in $LOG_FILE ==="

jq -r --arg lvl "$LEVEL" \
  'select(.level == $lvl) | "[\(.ts)] [\(.svc)] \(.msg)"' \
  "$LOG_FILE"

# Count errors per service
echo ""
echo "=== Error count by service ==="
jq -r --arg lvl "$LEVEL" \
  'select(.level == $lvl) | .svc' "$LOG_FILE" \
  | sort | uniq -c | sort -rn

知识检查:jq select() 的行为

测试您对 select() 在 jq 流水线中工作方式的理解。

给定以下命令:

echo '[{"name":"Alice","age":30},{"name":"Bob","age":17},{"name":"Carol","age":22}]' | jq '[.[] | select(.age >= 18) | .name]'

输出结果是什么?

课程回顾:使用 jq 流水线过滤 JSON

您已经学习了用于在命令行中导航和过滤 JSON 的 jq 核心工具:

  • 点号表示法(.field、.a.b.c)用于从对象中选择字段
  • 数组访问(.[0]、.[])用于索引数组和遍历数组
  • 管道运算符(|)用于连接过滤器;每个阶段都会处理前一阶段输出的所有值
  • select(cond) 用于过滤值,只传递条件为真值的值
  • 对象/数组构造({}、[]、map())用于将数据重塑为新的结构
  • 替代运算符(//)用于为 null 或缺失字段提供默认值
  • -r 标志用于在 shell 变量赋值时去除引号;--arg 用于安全地注入 shell 变量
  • 递归下降(.. | .field?)用于在路径未知时搜索深层嵌套的树状结构

借助这些基础构件,您可以将任何 JSON API 响应、日志文件或配置转换为脚本所需的确切数据,而无需离开终端。

常见问题解答

「使用 jq 管道筛选和选择 JSON」课时是免费的吗?

是的 — 「使用 jq 管道筛选和选择 JSON」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 DevOps Bootcamp 课程的其余内容,请升级到 CoddyKit PRO。 DevOps Bootcamp 课程共包含 4 节课。

「使用 jq 管道筛选和选择 JSON」这节课中我会学到什么?

使用 jq 选择器、管道和 select 过滤器浏览嵌套对象与数组。 你通过在浏览器中直接运行的动手代码来练习 DevOps Bootcamp,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 DevOps Bootcamp 需要有经验吗?

无需任何先前经验。CoddyKit 上的 DevOps Bootcamp 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。

「使用 jq 管道筛选和选择 JSON」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 DevOps Bootcamp 课中编写并运行代码吗?

能。每节 DevOps Bootcamp 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 使用 jq 管道筛选和选择 JSON
  2. 使用 jq 转换和构建 JSON 对象
  3. 结合 curl 与 jq 调用 REST API
  4. 使用 yq 编辑 YAML 配置文件
← 返回 DevOps Bootcamp