jq?想象一下你用 curl 请求一个 API,返回了 500 行的 JSON 数据,但你只想要其中一个字段的值。jq 就是用来解决这个问题的。
核心功能:
grep 一样,只选择符合条件的数据。awk 一样,重组数据,创建新的 JSON 结构。Debian / Ubuntu:
1sudo apt update2sudo apt install jqCentOS / RHEL / Fedora:
xxxxxxxxxx31sudo dnf install jq2# (旧版使用 yum)3# sudo yum install jqmacOS (via Homebrew):
xxxxxxxxxx11brew install jqjq 通过“过滤器”(filter)表达式来工作。它从 stdin(标准输入)读取 JSON 数据,应用过滤器,然后将结果输出到 stdout(标准输出)。
基本语法:
x1# 方式一:使用管道2cat data.json | jq '[filter]'34# 方式二:直接读取文件5jq '[filter]' data.json示例数据 (users.json)在本教程中,我们将使用以下 users.json 文件:
xxxxxxxxxx271{2 "project": "Demo",3 "last_updated": "2025-10-28T10:00:00Z",4 "users": [5 {6 "id": 1,7 "name": "Alice",8 "age": 28,9 "isActive": true,10 "skills": ["python", "sql", "cloud"]11 },12 {13 "id": 2,14 "name": "Bob",15 "age": 35,16 "isActive": false,17 "skills": ["java", "javascript", "docker"]18 },19 {20 "id": 3,21 "name": "Charlie",22 "age": 42,23 "isActive": true,24 "skills": ["go", "kubernetes"]25 }26 ]27}.)'.' 过滤器表示“整个输入”。这是 jq 最简单的用途:格式化 JSON。
xxxxxxxxxx21# 假设 users.json 是压缩的2jq '.' users.json(输出会是上面那个格式化好的 users.json)
.key)使用 .key 来访问对象的字段。
xxxxxxxxxx71# 获取 "project" 字段2jq '.project' users.json3# 输出: "Demo"45# 获取 "users" 字段 (会输出整个数组)6jq '.users' users.json7# 输出: [...] (包含三个用户对象的数组).[index])使用 .[index] 访问数组元素(索引从 0 开始)。
xxxxxxxxxx71# 获取 users 数组的第一个元素2jq '.users[0]' users.json3# 输出: { "id": 1, "name": "Alice", ... }45# 获取第一个用户的名字6jq '.users[0].name' users.json7# 输出: "Alice".[]) ⭐这是 jq 最核心的概念之一。当在一个数组上使用 .[] 时,它会将数组“解包”(unpack)成一个数据流 (stream),即将数组中的每个元素作为独立的 JSON 输出。
xxxxxxxxxx21# 将 users 数组展开为数据流2jq '.users[]' users.json输出: (注意,这不是一个数组了,而是三个独立的对象)
xxxxxxxxxx211{2 "id": 1,3 "name": "Alice",4 "age": 28,5 "isActive": true,6 "skills": ["python", "sql", "cloud"]7}8{9 "id": 2,10 "name": "Bob",11 "age": 35,12 "isActive": false,13 "skills": ["java", "javascript", "docker"]14}15{16 "id": 3,17 "name": "Charlie",18 "age": 42,19 "isActive": true,20 "skills": ["go", "kubernetes"]21}|) 与数据流jq 内部的 | 管道符,和 Shell 的管道符 | 概念一致:将左侧表达式的输出,作为右侧表达式的输入。
这允许你将简单的过滤器链接起来,构建复杂的查询。
.[] 和 .key目标:获取所有用户的名字。
.users:获取 users 数组。| .[]:将数组解包成 3 个用户对象流。| .name:对流中的每一个对象应用 .name 过滤器。xxxxxxxxxx11jq '.users[] | .name' users.json输出:
xxxxxxxxxx31"Alice"2"Bob"3"Charlie"
,)逗号 (,) 用于将两个过滤器应用于同一个输入,并把它们的结果合并到一个流中。
xxxxxxxxxx21# 获取第一个用户的名字和年龄2jq '.users[0] | .name, .age' users.json输出:
xxxxxxxxxx21"Alice"228
select)select(expression) 是 jq 的 grep。它只保留那些使 expression 结果为 true 的输入。
目标:只获取所有“活跃”(isActive) 的用户。
.users[]:获取用户流。| select(.isActive == true):对流中的每个对象进行检查,只保留 isActive 为 true 的对象。xxxxxxxxxx11jq '.users[] | select(.isActive == true)' users.json输出: (只返回 Alice 和 Charlie 的对象)
xxxxxxxxxx141{2 "id": 1,3 "name": "Alice",4 "age": 28,5 "isActive": true,6 7}8{9 "id": 3,10 "name": "Charlie",11 "age": 42,12 "isActive": true,13 14}目标:获取所有“活跃”用户的“名字”。我们只需在上面的基础上,再加一个管道:
xxxxxxxxxx11jq '.users[] | select(.isActive == true) | .name' users.json输出:
xxxxxxxxxx21"Alice"2"Charlie"
目标:获取年龄大于 30 且会用 "docker" 的用户。(注意:select 内部可以使用 and, or。检查数组包含使用 any)
xxxxxxxxxx21# .skills | any(. == "docker") 检查 skills 数组是否包含 "docker"2jq '.users[] | select(.age > 30 and (.skills | any(. == "docker")))' users.json输出:
xxxxxxxxxx61{2 "id": 2,3 "name": "Bob",4 "age": 35,5 6}jq 不仅能过滤,还能创建新的 JSON 对象 {} 和数组 []。
目标:获取所有用户的列表,但只包含他们的名字和技能数量。
.users[]:获取用户流。
| { ... }:对流中的每个对象,构建一个新对象。
username: .name:在新对象中创建 username 键,值为原对象的 name 键。skill_count: (.skills | length):在新对象中创建 skill_count 键,值为原对象 skills 数组的长度 (length 是一个内置函数)。xxxxxxxxxx11jq '.users[] | { username: .name, skill_count: (.skills | length) }' users.json输出: (三个新构造的对象流)
xxxxxxxxxx121{2 "username": "Alice",3 "skill_count": 34}5{6 "username": "Bob",7 "skill_count": 38}9{10 "username": "Charlie",11 "skill_count": 212}目标:将上述结果重新包装成一个数组。只需在整个过滤器表达式两边加上 [ 和 ]。
xxxxxxxxxx11jq '[ .users[] | { username: .name, skill_count: (.skills | length) } ]' users.json输出: (一个包含三个新对象的数组)
xxxxxxxxxx141[2 {3 "username": "Alice",4 "skill_count": 35 },6 {7 "username": "Bob",8 "skill_count": 39 },10 {11 "username": "Charlie",12 "skill_count": 213 }14]jq 是为 Shell 脚本而生的。有两个关键标志:-r 和 --arg。
-r (--raw-output):移除引号默认情况下,jq 输出的字符串会带引号 ("Alice")。这在 Shell 中非常不便。-r 标志会移除字符串两边的引号。
xxxxxxxxxx71# 默认情况2jq '.users[0].name' users.json3# 输出: "Alice"45# 使用 -r6jq -r '.users[0].name' users.json7# 输出: Alice在 Shell 脚本中使用:
xxxxxxxxxx91# 错误的方式2USERNAME=$(jq '.users[0].name' users.json)3echo "Hello, $USERNAME"4# 输出: Hello, "Alice"56# 正确的方式7USERNAME=$(jq -r '.users[0].name' users.json)8echo "Hello, $USERNAME"9# 输出: Hello, Alice--arg:将 Shell 变量传入 jq ⭐千万不要试图用 $ 拼接 Shell 变量来构建 jq 过滤器,这很脆弱且不安全。正确的方法是使用 --arg。
语法: --arg <jq内部变量名> <Shell变量的值>
目标:在 Shell 脚本中查找指定 ID 的用户。
xxxxxxxxxx22123# 要查找的 ID4LOOKUP_ID=256# 错误的方式 (字符串拼接)7# jq '.users[] | select(.id == '$LOOKUP_ID')' users.json89# 正确的方式:使用 --arg10# 1. --arg user_id "$LOOKUP_ID"11# - 创建一个 jq 内部变量 $user_id12# - 它的值是 Shell 变量 $LOOKUP_ID (即 2)13#14# 2. 注意:$LOOKUP_ID (Shell变量) 传入后变成了 $user_id (jq变量)。15#16# 3. --arg 传入的是字符串。如果需要比较数字,17# 最好使用 --argjson,或者在 jq 内部转换:.id == ($user_id | tonumber)1819# 简单起见,我们用 --argjson (它会保留类型,2 还是 2)20jq --argjson user_id "$LOOKUP_ID" \21 '.users[] | select(.id == $user_id)' \22 users.json输出:
xxxxxxxxxx51{2 "id": 2,3 "name": "Bob",4 5}jq 快速备忘录| 过滤器/标志 | 示例 | 描述 |
|---|---|---|
. | jq '.' data.json | 完整输入 (用于美化) |
.key | jq '.users' data.json | 访问对象键 |
.[N] | jq '.users[0]' data.json | 访问数组索引 |
.[] | jq '.users[]' data.json | 将数组解包为数据流 |
| | jq '.users[] | .name' | 管道:将左侧输出传给右侧 |
select(...) | jq '... | select(.age > 30)' | 过滤:只保留满足条件的 |
{} | jq '... | {n: .name, a: .age}' | 构造新对象 |
[] | jq '[ .users[] | .name ]' | 构造新数组 (收集流) |
length | jq '.users | length' | 返回数组或字符串的长度 |
keys | jq '.users[0] | keys' | 返回对象的所有键 (作为数组) |
-r | jq -r '.users[0].name' | Raw Output:移除字符串的引号 |
--arg name val | jq --arg u "Bob" '... | select(.name == $u)' | 传入 Shell 变量 (作为字符串) |
--argjson n v | jq --argjson id 2 '... | select(.id == $id)' | 传入 Shell 变量 (保留 JSON 类型) |