跳到主要内容

jq 核心知识点

第一步:准备示例文本文件 (data.json)

请将以下内容保存为一个名为 data.json 的文本文件。这个文件模拟了一个包含系统、服务和状态信息的典型 JSON 响应。

{
"system_id": "prod-web-cluster-01",
"timestamp": "2023-10-27T14:30:00Z",
"region": "us-east-1",
"status": "HEALTHY",
"monitoring_enabled": true,
"load_balancer": {
"type": "ALB",
"dns_name": "lb-1234567890abcdef0.us-east-1.elb.amazonaws.com",
"healthy_hosts": 2,
"unhealthy_hosts": 0
},
"servers": [
{
"id": "i-0a1b2c3d4e5f6",
"ip_address": "10.0.1.15",
"type": "web",
"status": "running",
"cpu_usage_percent": 15.5,
"memory_mb": 8192,
"tags": ["primary", "nginx"]
},
{
"id": "i-0f6e5d4c3b2a1",
"ip_address": "10.0.1.28",
"type": "web",
"status": "running",
"cpu_usage_percent": 22.0,
"memory_mb": 8192,
"tags": ["secondary", "nginx"]
},
{
"id": "i-07a8b9c0d1e2f",
"ip_address": "10.0.2.55",
"type": "db-replica",
"status": "stopped",
"cpu_usage_percent": null,
"memory_mb": 16384,
"tags": ["mysql", "readonly"]
}
],
"active_alerts": null,
"maintenance_window": {
"start": "Sun, 03:00 UTC",
"duration_hours": 2
}
}

第二步:学习 jq 核心功能及典型任务

jq 的基本工作模式是:读取 JSON 输入 -> 应用过滤器 (filter) -> 输出结果

基础语法: jq '[过滤器]' [输入文件...]

  • 过滤器是用单引号 ' ' 包裹的 jq 表达式。
  • 如果省略输入文件,jq 会从标准输入读取。

基础:美化输出与 Identity Filter (.)

最简单的过滤器是 .,它表示整个输入对象,jq 默认会美化输出。

  1. 任务: 美化并打印整个 JSON 文件。
    • 命令:
      jq '.' data.json
    • 解释: . 是 identity filter,它不做任何改变,只是将输入原样输出,jq 默认会进行格式化和着色(如果终端支持)。这是检查 JSON 结构的好方法。

场景 1: 访问对象字段

使用 .key_name 访问对象的字段。如果 key 包含特殊字符,使用 ."key-name"

  1. 任务: 获取 system_id 的值。

    • 命令:
      jq '.system_id' data.json
    • 解释: .system_id 选择顶级对象下的 system_id 字段。
  2. 任务: 获取负载均衡器的 dns_name

    • 命令:
      jq '.load_balancer.dns_name' data.json
    • 解释: 链式调用,先访问 load_balancer 对象,再访问其下的 dns_name 字段。
  3. 任务: 获取维护窗口的开始时间。

    • 命令:
      jq '.maintenance_window.start' data.json
    • 解释: 访问嵌套对象 maintenance_windowstart 字段。

场景 2: 访问数组元素与切片

使用 .key[index] 访问数组元素(索引从 0 开始)。

  1. 任务: 获取第一个服务器对象。

    • 命令:
      jq '.servers[0]' data.json
    • 解释: .servers 选择 servers 数组,[0] 选择该数组的第一个元素。
  2. 任务: 获取第一个服务器的 ip_address

    • 命令:
      jq '.servers[0].ip_address' data.json
    • 解释: 先选择第一个服务器对象,然后从中选择 ip_address 字段。
  3. 任务: 获取最后一个服务器对象。

    • 命令:
      jq '.servers[-1]' data.json
    • 解释: 支持负数索引,-1 表示最后一个元素。
  4. 任务: 获取前两个服务器对象(切片)。

    • 命令:
      jq '.servers[0:2]' data.json
    • 解释: [start:end] 进行切片,包含 start 索引,不包含 end 索引。输出会是一个包含这两个对象的数组。

场景 3: 迭代与提取 (.[], |)

使用 .key[] 将数组或对象的元素/值展开成一个序列流。使用管道符 | 将前一个过滤器的输出传递给下一个过滤器。

  1. 任务: 列出所有服务器的 id

    • 命令:
      jq '.servers[].id' data.json
    • 解释: .servers[]servers 数组中的每个对象输出为一个独立的流。对于流中的每个对象,.id 提取其 id 字段。结果是每个 ID 占一行。
  2. 任务: 列出所有服务器的 ip_addressstatus

    • 命令:
      jq '.servers[] | {ip: .ip_address, status: .status}' data.json
    • 解释: .servers[] 展开数组。管道 | 将每个服务器对象传递给 {ip: .ip_address, status: .status}。这个过滤器为每个输入的服务器对象创建一个新的 JSON 对象,包含 ipstatus 两个键。
  3. 任务: 列出第一个服务器的所有标签 (tags)。

    • 命令:
      jq '.servers[0].tags[]' data.json
    • 解释: .servers[0] 选择第一个服务器对象,.tags[] 展开其 tags 数组,将每个标签输出为独立的字符串流。

场景 4: 构建新的 JSON 对象或数组

使用 {} 构建对象,使用 [] 构建数组。可以在其中嵌套过滤器。

  1. 任务: 创建一个只包含 system_idregion 的新对象。

    • 命令:
      jq '{sys_id: .system_id, location: .region}' data.json
    • 解释: {key1: filter1, key2: filter2} 创建一个新对象。sys_idlocation 是新对象的键,.system_id.region 是从原始输入中提取值的过滤器。
  2. 任务: 创建一个包含所有服务器 IP 地址的数组。

    • 命令:
      jq '[ .servers[].ip_address ]' data.json
    • 解释: [...] 用于构建数组。.servers[].ip_address 生成一个 IP 地址流,[] 将这个流收集到一个数组中。
  3. 任务: 为每个服务器创建一个对象,包含 idmemory_gb (将 MB 转换为 GB,近似)。

    • 命令:
      jq '.servers[] | {id: .id, memory_gb: (.memory_mb / 1024)}' data.json
    • 解释: 在构建新对象时,可以进行计算。.memory_mb / 1024 计算 GB 值。注意需要括号包围计算表达式。

场景 5: 使用函数进行过滤 (select())

select(boolean_expression) 函数只保留输入流中使表达式为 true 的元素。

  1. 任务: 找出所有状态为 "running" 的服务器对象。

    • 命令:
      jq '.servers[] | select(.status == "running")' data.json
    • 解释: .servers[] 展开数组。select(.status == "running") 对每个服务器对象进行判断,只输出 status 字段等于 "running" 的对象。
  2. 任务: 找出 CPU 使用率大于 20% 的服务器的 ID。

    • 命令:
      jq '.servers[] | select(.cpu_usage_percent != null and .cpu_usage_percent > 20) | .id' data.json
    • 解释: 先展开,然后 select 过滤。注意要先检查 cpu_usage_percent 不为 null 再进行比较,否则 null > 20 会出错。最后提取 id
  3. 任务: 找出所有包含 "nginx" 标签的服务器的 IP 地址。

    • 命令:
      jq '.servers[] | select(.tags | contains(["nginx"])) | .ip_address' data.json
    • 解释: contains(element_or_array) 检查数组是否包含某个元素或子数组。这里检查 .tags 数组是否包含 "nginx" 字符串。

场景 6: 使用内置函数 (如 length, keys, has)

jq 有许多内置函数。

  1. 任务: 计算有多少个服务器。

    • 命令:
      jq '.servers | length' data.json
    • 解释: length 函数可以计算数组的长度或字符串的长度。这里计算 servers 数组的长度。
  2. 任务: 获取顶层对象的所有键。

    • 命令:
      jq 'keys' data.json
    • 解释: keys 函数返回一个包含对象所有键的数组。
  3. 任务: 检查顶层对象是否有 active_alerts 字段。

    • 命令:
      jq 'has("active_alerts")' data.json
    • 解释: has(key) 检查当前对象(这里是顶级对象 .) 是否包含指定的键,返回 truefalse
  4. 任务: 获取所有服务器的 ID 列表(与场景 4 任务 2 类似,但这是另一种常见方法)。

    • 命令:
      jq '.servers | map(.id)' data.json
    • 解释: map(filter) 对输入数组的每个元素应用 filter,并返回结果组成的新数组。这里对 servers 数组中的每个元素应用 .id 过滤器。

场景 7: 处理输出格式 (-r, -c)

jq 的输出默认是格式化的 JSON。有时需要原始字符串或紧凑格式。

  1. 任务: 获取 system_id 的原始字符串值(不带引号)。

    • 命令:
      jq -r '.system_id' data.json
    • 解释: -r (raw output) 选项移除最外层的引号,输出纯文本字符串。这在脚本中赋值给变量时非常有用。
  2. 任务: 获取所有运行中服务器的 IP 地址,每个地址占一行,作为原始字符串。

    • 命令:
      jq -r '.servers[] | select(.status == "running") | .ip_address' data.json
    • 解释: 结合了过滤和 -r 选项,直接输出 IP 列表,方便后续处理(如 ssh 循环)。
  3. 任务: 将每个服务器对象输出为一行紧凑的 JSON(无换行和多余空格)。

    • 命令:
      jq -c '.servers[]' data.json
    • 解释: -c (compact output) 选项使每个 JSON 输出项(这里是每个服务器对象)都在单独的一行上,且内部没有不必要的空格。常用于处理 JSON Lines 格式 (ndjson)。

总结与建议

  • 从简单的字段访问 (.key) 和数组索引 (.[index]) 开始。
  • 掌握迭代 (.[]) 和管道 (|) 的组合,这是 jq 强大的核心。
  • 学习使用 select() 进行条件过滤。
  • 练习使用 {}[] 构建你需要的输出结构。
  • 熟悉 length, keys, map, contains 等常用函数。
  • 了解 -r-c 选项在脚本自动化中的重要性。
  • jq 的功能远不止这些,遇到复杂问题时,查阅 jq 手册 (man jq) 或在线文档/Cookbook 是非常有用的。

现在,你可以在你的电脑上创建 data.json 文件,然后逐一执行上面的命令,观察输出结果。尝试修改过滤器,组合不同的功能,这是掌握 jq 的最佳途径!祝你学习愉快!