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
默认会美化输出。
- 任务: 美化并打印整个 JSON 文件。
- 命令:
jq '.' data.json
- 解释:
.
是 identity filter,它不做任何改变,只是将输入原样输出,jq
默认会进行格式化和着色(如果终端支持)。这是检查 JSON 结构的好方法。
- 命令:
场景 1: 访问对象字段
使用 .key_name
访问对象的字段。如果 key 包含特殊字符,使用 ."key-name"
。
-
任务: 获取
system_id
的值。- 命令:
jq '.system_id' data.json
- 解释:
.system_id
选择顶级对象下的system_id
字段。
- 命令:
-
任务: 获取负载均衡器的
dns_name
。- 命令:
jq '.load_balancer.dns_name' data.json
- 解释: 链式调用,先访问
load_balancer
对象,再访问其下的dns_name
字段。
- 命令:
-
任务: 获取维护窗口的开始时间。
- 命令:
jq '.maintenance_window.start' data.json
- 解释: 访问嵌套对象
maintenance_window
的start
字段。
- 命令:
场景 2: 访问数组元素与切片
使用 .key[index]
访问数组元素(索引从 0 开始)。
-
任务: 获取第一个服务器对象。
- 命令:
jq '.servers[0]' data.json
- 解释:
.servers
选择servers
数组,[0]
选择该数组的第一个元素。
- 命令:
-
任务: 获取第一个服务器的
ip_address
。- 命令:
jq '.servers[0].ip_address' data.json
- 解释: 先选择第一个服务器对象,然后从中选择
ip_address
字段。
- 命令:
-
任务: 获取最后一个服务器对象。
- 命令:
jq '.servers[-1]' data.json
- 解释: 支持负数索引,
-1
表示最后一个元素。
- 命令:
-
任务: 获取前两个服务器对象(切片)。
- 命令:
jq '.servers[0:2]' data.json
- 解释:
[start:end]
进行切片,包含start
索引,不包含end
索引。输出会是一个包含这两个对象的数组。
- 命令:
场景 3: 迭代与提取 (.[]
, |
)
使用 .key[]
将数组或对象的元素/值展开成一个序列流。使用管道符 |
将前一个过滤器的输出传递给下一个过滤器。
-
任务: 列出所有服务器的
id
。- 命令:
jq '.servers[].id' data.json
- 解释:
.servers[]
将servers
数组中的每个对象输出为一个独立的流。对于流中的每个对象,.id
提取其id
字段。结果是每个 ID 占一行。
- 命令:
-
任务: 列出所有服务器的
ip_address
和status
。- 命令:
jq '.servers[] | {ip: .ip_address, status: .status}' data.json
- 解释:
.servers[]
展开数组。管道|
将每个服务器对象传递给{ip: .ip_address, status: .status}
。这个过滤器为每个输入的服务器对象创建一个新的 JSON 对象,包含ip
和status
两个键。
- 命令:
-
任务: 列出第一个服务器的所有标签 (
tags
)。- 命令:
jq '.servers[0].tags[]' data.json
- 解释:
.servers[0]
选择第一个服务器对象,.tags[]
展开其tags
数组,将每个标签输出为独立的字符串流。
- 命令:
场景 4: 构建新的 JSON 对象或数组
使用 {}
构建对象,使用 []
构建数组。可以在其中嵌套过滤器。
-
任务: 创建一个只包含
system_id
和region
的新对象。- 命令:
jq '{sys_id: .system_id, location: .region}' data.json
- 解释:
{key1: filter1, key2: filter2}
创建一个新对象。sys_id
和location
是新对象的键,.system_id
和.region
是从原始输入中提取值的过滤器。
- 命令:
-
任务: 创建一个包含所有服务器 IP 地址的数组。
- 命令:
jq '[ .servers[].ip_address ]' data.json
- 解释:
[...]
用于构建数组。.servers[].ip_address
生成一个 IP 地址流,[]
将这个流收集到一个数组中。
- 命令:
-
任务: 为每个服务器创建一个对象,包含
id
和memory_gb
(将 MB 转换为 GB,近似)。- 命令:
jq '.servers[] | {id: .id, memory_gb: (.memory_mb / 1024)}' data.json
- 解释: 在构建新对象时,可以进行计算。
.memory_mb / 1024
计算 GB 值。注意需要括号包围计算表达式。
- 命令:
场景 5: 使用函数进行过滤 (select()
)
select(boolean_expression)
函数只保留输入流中使表达式为 true
的元素。
-
任务: 找出所有状态为 "running" 的服务器对象。
- 命令:
jq '.servers[] | select(.status == "running")' data.json
- 解释:
.servers[]
展开数组。select(.status == "running")
对每个服务器对象进行判断,只输出status
字段等于"running"
的对象。
- 命令:
-
任务: 找出 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
。
- 命令:
-
任务: 找出所有包含 "nginx" 标签的服务器的 IP 地址。
- 命令:
jq '.servers[] | select(.tags | contains(["nginx"])) | .ip_address' data.json
- 解释:
contains(element_or_array)
检查数组是否包含某个元素或子数组。这里检查.tags
数组是否包含"nginx"
字符串。
- 命令:
场景 6: 使用内置函数 (如 length
, keys
, has
)
jq
有许多内置函数。
-
任务: 计算有多少个服务器。
- 命令:
jq '.servers | length' data.json
- 解释:
length
函数可以计算数组的长度或字符串的长度。这里计算servers
数组的长度。
- 命令:
-
任务: 获取顶层对象的所有键。
- 命令:
jq 'keys' data.json
- 解释:
keys
函数返回一个包含对象所有键的数组。
- 命令:
-
任务: 检查顶层对象是否有
active_alerts
字段。- 命令:
jq 'has("active_alerts")' data.json
- 解释:
has(key)
检查当前对象(这里是顶级对象.
) 是否包含指定的键,返回true
或false
。
- 命令:
-
任务: 获取所有服务器的 ID 列表(与场景 4 任务 2 类似,但这是另一种常见方法)。
- 命令:
jq '.servers | map(.id)' data.json
- 解释:
map(filter)
对输入数组的每个元素应用filter
,并返回结果组成的新数组。这里对servers
数组中的每个元素应用.id
过滤器。
- 命令:
场景 7: 处理输出格式 (-r
, -c
)
jq
的输出默认是格式化的 JSON。有时需要原始字符串或紧凑格式。
-
任务: 获取
system_id
的原始字符串值(不带引号)。- 命令:
jq -r '.system_id' data.json
- 解释:
-r
(raw output) 选项移除最外层的引号,输出纯文本字符串。这在脚本中赋值给变量时非常有用。
- 命令:
-
任务: 获取所有运行中服务器的 IP 地址,每个地址占一行,作为原始字符串。
- 命令:
jq -r '.servers[] | select(.status == "running") | .ip_address' data.json
- 解释: 结合了过滤和
-r
选项,直接输出 IP 列表,方便后续处理(如ssh
循环)。
- 命令:
-
任务: 将每个服务器对象输出为一行紧凑的 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
的最佳途径!祝你学习愉快!