文章目录
-
- [jq 命令详解](#jq 命令详解)
-
- 基本概念
- 语法
- 参数选项
- [安装 jq](#安装 jq)
- 准备测试文件
- 基本用法示例
-
- [1. 基本 JSON 解析](#1. 基本 JSON 解析)
- [2. 使用 -r 选项输出原始字符串](#2. 使用 -r 选项输出原始字符串)
- [3. 使用 -c 选项紧凑输出](#3. 使用 -c 选项紧凑输出)
- 数组操作
-
- [1. 访问数组元素](#1. 访问数组元素)
- [2. 数组长度和迭代](#2. 数组长度和迭代)
- [3. 数组切片](#3. 数组切片)
- [map 和 map_values 函数](#map 和 map_values 函数)
-
- [1. map 函数](#1. map 函数)
- [2. map_values 函数](#2. map_values 函数)
- [3. map vs map_values 对比](#3. map vs map_values 对比)
- 条件过滤
-
- [1. 使用 select 过滤](#1. 使用 select 过滤)
- [2. 多重条件过滤](#2. 多重条件过滤)
- [3. 模糊查询(类似 SQL LIKE)](#3. 模糊查询(类似 SQL LIKE))
- 数据转换
-
- [1. 创建新对象](#1. 创建新对象)
- [2. 数学运算](#2. 数学运算)
- [3. 字符串操作](#3. 字符串操作)
- 高级用法示例
-
- [1. 使用变量](#1. 使用变量)
- [2. 修改 JSON 数据](#2. 修改 JSON 数据)
- [3. 分组和排序](#3. 分组和排序)
- [4. 处理 API 响应](#4. 处理 API 响应)
- 实际应用场景
-
- 场景一:日志分析
- 场景二:配置文件处理
- [场景三:Docker 镜像处理](#场景三:Docker 镜像处理)
- [场景四:Kubernetes 资源处理](#场景四:Kubernetes 资源处理)
- [场景五:CSV 格式转换](#场景五:CSV 格式转换)
- 与其他命令结合使用
-
- [1. 与 curl 结合](#1. 与 curl 结合)
- 常见问题
-
- [Q1: 如何处理大型 JSON 文件?](#Q1: 如何处理大型 JSON 文件?)
- [Q2: 如何处理 JSON 数组外的多个 JSON 对象?](#Q2: 如何处理 JSON 数组外的多个 JSON 对象?)
- [Q3: 如何处理包含特殊字符的字符串?](#Q3: 如何处理包含特殊字符的字符串?)
- [Q4: jq 表达式太复杂怎么办?](#Q4: jq 表达式太复杂怎么办?)
- [Q5: 如何处理可能不存在的字段?](#Q5: 如何处理可能不存在的字段?)
- 性能优化技巧
-
- [1. 使用 -c 选项减少输出大小](#1. 使用 -c 选项减少输出大小)
- [2. 尽早过滤减少数据处理量](#2. 尽早过滤减少数据处理量)
- [3. 避免不必要的字段](#3. 避免不必要的字段)
- 总结
- 参考文档
jq 命令详解
jq 是一个轻量级且功能强大的命令行 JSON 处理器。它允许你以高效的方式解析、过滤、转换和格式化 JSON 数据,特别适合在 shell 脚本中处理 JSON 数据。
当你在终端中处理 JSON 格式的数据时,手动解析既繁琐又容易出错。jq 命令可以优雅地解决这个问题,让 JSON 处理变得简单直观。
bash
$ echo '{"name": "John", "age": 30}' | jq '.name'
"John"
$ echo '{"name": "John", "age": 30}' | jq '.age'
30
基本概念
jq 命令的主要功能是对 JSON 数据进行读取、解析、过滤、转换和格式化输出。它采用流式处理方式,可以高效处理任意大小的 JSON 数据。
- 输入:JSON 格式的文件或标准输入
- 处理:使用 jq 表达式进行查询、过滤、转换
- 输出:格式化后的 JSON 或其他指定格式
语法
bash
jq [选项] '过滤器表达式' [文件...]
或者从标准输入读取:
bash
cat data.json | jq '过滤器表达式'
参数选项
| 选项 | 说明 |
|---|---|
-c 或 --compact-output |
紧凑输出(不美化,单行显示) |
-r 或 --raw-output |
输出原始字符串(去除 JSON 引号) |
-s 或 --slurp |
将整个输入流读取到数组中 |
-M 或 --monochrome-output |
禁用颜色输出 |
--arg name value |
定义变量,值为字符串 |
--argjson name value |
定义变量,值为 JSON |
--slurpfile name file |
将文件内容读入变量(作为数组) |
-n 或 --null-input |
不使用输入,直接从参数构建输出 |
--help |
显示帮助信息 |
--version |
显示版本信息 |
安装 jq
在大多数 Linux 发行版中,可以通过包管理器安装 jq:
bash
# Ubuntu/Debian
sudo apt-get install jq
# CentOS/RHEL
sudo yum install jq
# Fedora
sudo dnf install jq
# macOS (使用 Homebrew)
brew install jq
安装完成后,验证安装:
bash
$ jq --version
jq-1.7.1
准备测试文件
为了演示方便,我们创建一个本地测试文件 users.json:
bash
cat > users.json << 'EOF'
[
{
"id": 1,
"name": "Alice",
"age": 28,
"email": "alice@example.com",
"isActive": true,
"address": {
"street": "123 Main St",
"city": "New York",
"zip": "10001"
},
"hobbies": ["reading", "hiking", "coding"]
},
{
"id": 2,
"name": "Bob",
"age": 35,
"email": "bob@example.com",
"isActive": false,
"address": {
"street": "456 Oak Ave",
"city": "Los Angeles",
"zip": "90001"
},
"hobbies": ["gaming", "music"]
},
{
"id": 3,
"name": "Charlie",
"age": 42,
"email": "charlie@example.com",
"isActive": true,
"address": {
"street": "789 Pine Rd",
"city": "Chicago",
"zip": "60601"
},
"hobbies": ["cooking", "traveling"]
}
]
EOF
基本用法示例
1. 基本 JSON 解析
bash
# 获取所有内容(美化输出)
$ jq '.' users.json
[
{
"id": 1,
"name": "Alice",
"age": 28,
"email": "alice@example.com",
"isActive": true,
"address": {
"street": "123 Main St",
"city": "New York",
"zip": "10001"
},
"hobbies": [
"reading",
"hiking",
"coding"
]
},
...
]
# 获取特定字段
$ jq '.[0].name' users.json
"Alice"
# 获取嵌套字段
$ jq '.[0].address.city' users.json
"New York"
2. 使用 -r 选项输出原始字符串
bash
# 不带 -r 选项(带引号)
$ jq '.[0].name' users.json
"Alice"
# 带 -r 选项(去除引号)
$ jq -r '.[0].name' users.json
Alice
3. 使用 -c 选项紧凑输出
bash
# 默认美化输出
$ jq '.[0]' users.json
{
"id": 1,
"name": "Alice",
"age": 28,
...
}
# 紧凑输出(单行)
$ jq -c '.[0]' users.json
{"id":1,"name":"Alice","age":28,"email":"alice@example.com","isActive":true,...}
数组操作
1. 访问数组元素
bash
# 获取第一个用户
$ jq '.[0]' users.json
# 获取前两个用户
$ jq '.[0:2]' users.json
# 获取所有用户的姓名
$ jq '.[].name' users.json
"Alice"
"Bob"
"Charlie"
# 使用 -r 去掉引号
$ jq -r '.[].name' users.json
Alice
Bob
Charlie
2. 数组长度和迭代
bash
# 获取数组长度
$ jq 'length' users.json
3
# 获取每个用户的爱好数量
$ jq '.[] | .hobbies | length' users.json
3
2
2
# 获取第一个用户的所有爱好
$ jq '.[0].hobbies[]' users.json
"reading"
"hiking"
"coding"
3. 数组切片
bash
# 获取前两个用户
$ jq '.[0:2]' users.json
# 获取第 2 到第 3 个用户
$ jq '.[1:3]' users.json
# 获取最后一个用户
$ jq '.[-1]' users.json
map 和 map_values 函数
map 和 map_values 是 jq 中非常强大的函数,用于对数组或对象的每个元素应用相同的转换。
1. map 函数
map(x) 对输入数组 的每个元素应用过滤器 x,并在新数组中返回结果。它等价于 [.[] | x]。
基本用法:
bash
# 对数组每个元素加 1
$ echo '[1, 2, 3, 4, 5]' | jq 'map(. * 2)'
[
2,
4,
6,
8,
10
]
# 对数组每个元素应用字符串转换
$ echo '[1, 2, 3]' | jq 'map(. | tostring)'
[
"1",
"2",
"3"
]
# 获取所有用户的姓名(等价于 [.[].name])
$ jq 'map(.name)' users.json
[
"Alice",
"Bob",
"Charlie"
]
# 获取所有用户的年龄并加 1
$ jq 'map(.age + 1)' users.json
[
29,
36,
43
]
复杂转换:
bash
# 为每个用户添加新字段
$ jq 'map(. + {country: "USA"})' users.json
[
{
"id": 1,
"name": "Alice",
"age": 28,
"email": "alice@example.com",
"isActive": true,
"address": {
"street": "123 Main St",
"city": "New York",
"zip": "10001"
},
"hobbies": ["reading", "hiking", "coding"],
"country": "USA"
},
...
]
# 提取每个用户的爱好数量
$ jq 'map(.hobbies | length)' users.json
[
3,
2,
2
]
# 获取每个用户的信息摘要
$ jq 'map({name: .name, city: .address.city, hobbyCount: (.hobbies | length)})' users.json
[
{
"name": "Alice",
"city": "New York",
"hobbyCount": 3
},
{
"name": "Bob",
"city": "Los Angeles",
"hobbyCount": 2
},
{
"name": "Charlie",
"city": "Chicago",
"hobbyCount": 2
}
]
与 select 结合使用:
bash
# 筛选出年龄大于 30 的用户(两种方式)
# 方式 1:先过滤再收集
$ jq '[.[] | select(.age > 30)]' users.json
# 方式 2:使用 map + select(注意:这会保留所有元素,不满足条件的会被过滤掉)
$ jq 'map(select(.age > 30))' users.json
# 上面的写法实际上会返回一个数组,其中包含满足条件的元素
# 正确的用法是对数组整体操作
$ echo '[1, 5, 3, 0, 7]' | jq 'map(select(. >= 2))'
[
5,
3,
7
]
特殊情况:
bash
# 使用 empty 产生空数组
$ echo '[1]' | jq 'map(empty)'
[]
# 复制元素
$ echo '[1]' | jq 'map(., .)'
[
1,
1
]
2. map_values 函数
map_values(x) 对 对象 的每个值应用过滤器 x。它被定义为 .[] |= x,是处理对象值的强大工具。
对对象使用:
bash
# 创建测试数据
cat > test_obj.json << 'EOF'
{
"a": 1,
"b": 2,
"c": 3
}
EOF
# 对每个值加 1
$ jq 'map_values(. + 1)' test_obj.json
{
"a": 2,
"b": 3,
"c": 4
}
# 将所有值转换为字符串
$ jq 'map_values(tostring)' test_obj.json
{
"a": "1",
"b": "2",
"c": "3"
}
# 对所有值乘以 10
$ jq 'map_values(. * 10)' test_obj.json
{
"a": 10,
"b": 20,
"c": 30
}
实际应用示例:
bash
# 将用户数组转换为对象后使用 map_values
# 首先用 INDEX 按 ID 构建对象,然后对每个用户的 age 字段加 1
$ jq 'INDEX(.id) | map_values(.age + 1)' users.json
{
"1": 29,
"2": 36,
"3": 43
}
# 提取每个用户的姓名
$ jq 'INDEX(.id) | map_values(.name)' users.json
{
"1": "Alice",
"2": "Bob",
"3": "Charlie"
}
删除值为 null 或空的字段:
bash
# 创建包含 null 值的测试数据
cat > with_nulls.json << 'EOF'
{
"name": "Alice",
"email": null,
"age": 28,
"phone": ""
}
EOF
# 删除值为 null 的字段
$ jq 'map_values(select(. != null))' with_nulls.json
{
"name": "Alice",
"age": 28,
"phone": ""
}
# 删除空字符串和 null 值
$ jq 'map_values(select(. != null and . != ""))' with_nulls.json
{
"name": "Alice",
"age": 28
}
注意事项:
虽然 map_values 也可以在数组上使用(行为类似于 map),但这不是它的主要用途。对于数组转换,应优先使用 map()。
bash
# 不推荐:在数组上使用 map_values
$ echo '[1, 2, 3]' | jq 'map_values(. + 1)' # 虽然能工作,但不是最佳实践
# 推荐:在数组上使用 map
$ echo '[1, 2, 3]' | jq 'map(. + 1)' # 更清晰、更符合语义
3. map vs map_values 对比
| 特性 | map(x) | map_values(x) |
|---|---|---|
| 等价形式 | `[.[] | x]` |
| 主要用途 | 数组转换 | 对象值转换或 数组(不推荐) |
| 返回值 | 新数组 | 修改后的对象或数组 |
| 多值处理 | 保留所有输出 | 每个元素只取第一个输出 |
| 适用场景 | 需要构建新数组时 | 需要修改现有结构时 |
使用建议:
- 对于数组 ,优先使用
map(), - 对于对象 ,使用
map_values()来转换所有值 - 当过滤器可能产生多个值时,使用
map()会保留所有值,而map_values()只取第一个
条件过滤
1. 使用 select 过滤
bash
# 筛选活跃用户
$ jq '.[] | select(.isActive == true)' users.json
# 筛选年龄大于 30 的用户
$ jq '.[] | select(.age > 30)' users.json
# 筛选居住在 New York 的用户
$ jq '.[] | select(.address.city == "New York")' users.json
# 组合条件
$ jq '.[] | select(.isActive == true and .age > 30)' users.json
2. 多重条件过滤
bash
# 筛选活跃且年龄大于 30 的用户
$ jq '[.[] | select(.isActive and .age > 30)]' users.json
# 筛选爱好包含 reading 的用户
$ jq '.[] | select(.hobbies | index("reading"))' users.json
3. 模糊查询(类似 SQL LIKE)
jq 没有直接的 LIKE 操作符,但可以使用 contains() 和 test() 函数来实现模糊匹配。
contains() 是基于"子串包含"的简单匹配,而 test() 是基于"正则表达式"的复杂匹配。
准备测试数据:
bash
cat > products.json << 'EOF'
[
{"id": 1, "name": "iPhone 15 Pro Max", "category": "Electronics", "price": 9999},
{"id": 2, "name": "MacBook Pro 14", "category": "electronics", "price": 14999},
{"id": 2.1, "name": "MacBook Pro", "category": "electronics", "price": 14999},
{"id": 3, "name": "Nike Air Max", "category": "Sports", "price": 899},
{"id": 4, "name": "Apple Watch Ultra", "category": "Electronics", "price": 6299},
{"id": 5, "name": "Adidas Running Shoes", "category": "Sports", "price": 699},
{"id": 6, "name": "iPad Air", "category": "Electronics", "price": 4799},
{"id": 7, "name": "Python Programming Book", "category": "Books", "price": 89},
{"id": 8, "name": "JavaScript Guide", "category": "Books", "price": 79}
]
EOF
jq 模糊查询与 SQL LIKE 对比表:
| SQL LIKE 语法 | jq 等价写法 | 说明 |
|---|---|---|
WHERE col LIKE '%abc%' |
`select(.col | contains("abc"))` |
WHERE col LIKE '%abc%' (忽略大小写) |
`select(.col | test("abc"; "i"))` |
WHERE col LIKE 'abc%' |
`select(.col | test("^abc"))` |
WHERE col LIKE '%abc' |
`select(.col | test("abc$"))` |
WHERE col LIKE 'a_c' (单字符通配) |
`select(.col | test("^a.c$"))` |
WHERE col LIKE 'a%' (多字符通配) |
`select(.col | test("^a.*"))` |
示例 1:包含匹配(LIKE '%abc%')
bash
# 查找名称包含 "Pro" 的产品(区分大小写)
$ jq '.[] | select(.name | contains("Pro"))' products.json
{
"id": 1,
"name": "iPhone 15 Pro Max",
"category": "Electronics",
"price": 9999
}
{
"id": 2,
"name": "MacBook Pro 14",
"category": "Electronics",
"price": 14999
}
# 查找类别包含 "elec" 的产品(不区分大小写)
$ jq '.[] | select(.category | test("elec"; "i"))' products.json
{
"id": 1,
"name": "iPhone 15 Pro Max",
"category": "Electronics",
"price": 9999
}
{
"id": 2,
"name": "MacBook Pro 14",
"category": "Electronics",
"price": 14999
}
{
"id": 4,
"name": "Apple Watch Ultra",
"category": "Electronics",
"price": 6299
}
{
"id": 6,
"name": "iPad Air",
"category": "Electronics",
"price": 4799
}
示例 2:前缀匹配(LIKE 'abc%')
bash
# 查找名称以 "Apple" 开头的产品
$ jq '.[] | select(.name | test("^Apple"))' products.json
{
"id": 4,
"name": "Apple Watch Ultra",
"category": "Electronics",
"price": 6299
}
# 查找类别以 "E" 开头的产品(不区分大小写)
$ jq '.[] | select(.category | test("^E"; "i"))' products.json
{
"id": 1,
"name": "iPhone 15 Pro Max",
"category": "Electronics",
"price": 9999
}
{
"id": 2,
"name": "MacBook Pro 14",
"category": "Electronics",
"price": 14999
}
{
"id": 4,
"name": "Apple Watch Ultra",
"category": "Electronics",
"price": 6299
}
{
"id": 6,
"name": "iPad Air",
"category": "Electronics",
"price": 4799
}
示例 3:后缀匹配(LIKE '%abc')
bash
# 查找名称以 "Pro" 结尾的产品 区分大小写
$ jq '.[] | select(.name | test("Pro$"))' products.json
{
"id": 2.1,
"name": "MacBook Pro",
"category": "electronics",
"price": 14999
}
# 查找名称以 "es" 结尾的产品(不区分大小写)
$ jq '.[] | select(.name | test("ES$"; "i"))' products.json
{
"id": 5,
"name": "Adidas Running Shoes",
"category": "Sports",
"price": 699
}
示例 4:单字符通配(LIKE 'a_c')
bash
# 查找名称为 3 个字符且以 "A" 开头、"e" 结尾的产品
# 正则表达式中 . 代表任意单个字符
$ jq '.[] | select(.name | test("^A.e$"))' products.json
# 无匹配结果(因为没有恰好 3 个字符的产品名)
# 查找类别为 5 个字符且以 "B" 开头、"s" 结尾的
$ jq -c '.[] | select(.category | test("^B..ks$"))' products.json
{
"id": 7,
"name": "Python Programming Book",
"category": "Books",
"price": 89
}
{
"id": 8,
"name": "JavaScript Guide",
"category": "Books",
"price": 79
}
示例 5:多字符通配(LIKE 'a%')
bash
# 查找名称以 "i" 开头的所有产品
# 正则表达式中 .* 代表任意多个字符
$ jq -c '.[] | select(.name | test("^i.*"))' products.json
{"id":1,"name":"iPhone 15 Pro Max","category":"Electronics","price":9999}
{"id":6,"name":"iPad Air","category":"Electronics","price":4799}
# 查找名称中 包含数字的产品 test 可以放任何正则
$ jq '.[] | select(.name | test("[0-9]"))' products.json
{
"id": 1,
"name": "iPhone 15 Pro Max",
"category": "Electronics",
"price": 9999
}
{
"id": 2,
"name": "MacBook Pro 14",
"category": "Electronics",
"price": 14999
}
示例 6:复杂正则表达式
bash
# 查找名称以 "P" 或 "p" 开头的产品(不区分大小写)
$ jq '.[] | select(.name | test("^[Pp]"))' products.json
{
"id": 7,
"name": "Python Programming Book",
"category": "Books",
"price": 89
}
# 查找类别是 "Electronics" 或 "sports" 的产品, 这里是区分大小写的
$ jq '.[] | select(.category | test("^(Electronics|sports)$"))' products.json
# 返回所有 Electronics 和 Sports 类别的产品
{
"id": 1,
"name": "iPhone 15 Pro Max",
"category": "Electronics",
"price": 9999
}
{
"id": 4,
"name": "Apple Watch Ultra",
"category": "Electronics",
"price": 6299
}
{
"id": 6,
"name": "iPad Air",
"category": "Electronics",
"price": 4799
}
# 查找名称长度在 10-15 个字符之间的产品
$ jq -c '.[] | select(.name | test("^.{10,15}$"))' products.json
{"id":2,"name":"MacBook Pro 14","category":"electronics","price":14999}
{"id":2.1,"name":"MacBook Pro","category":"electronics","price":14999}
{"id":3,"name":"Nike Air Max","category":"Sports","price":899}
注意事项:
-
contains()vstest():contains(): 简单的字符串包含检查,区分大小写,不支持正则test(): 支持正则表达式,功能更强大
-
性能考虑:
contains()通常比test()快- 对于简单包含检查,优先使用
contains()
-
转义特殊字符:
- 使用
test()时,如果需要匹配正则特殊字符(如.,*,?等),需要进行转义
- 使用
bash
# 查找包含点号的文件名
$ echo '{"file": "config.json"}' | jq 'select(.file | test("\\."))'
数据转换
1. 创建新对象
bash
# 只提取姓名和城市
$ jq '.[] | {name: .name, city: .address.city}' users.json
{
"name": "Alice",
"city": "New York"
}
{
"name": "Bob",
"city": "Los Angeles"
}
{
"name": "Charlie",
"city": "Chicago"
}
# 创建自定义结构,自定义结构
$ jq -c '.[] | {
fullName: .name,
ageGroup: (if .age < 30 then "young" elif .age < 40 then "middle" else "senior" end),
location: .address.city
}' users.json
# 结果如下:
{"fullName":"Alice","ageGroup":"young","location":"New York"}
{"fullName":"Bob","ageGroup":"middle","location":"Los Angeles"}
{"fullName":"Charlie","ageGroup":"senior","location":"Chicago"}
2. 数学运算
bash
# 计算所有用户的年龄总和
# .:代表当前的输入数据(即整个 JSON 数组)。
# []:迭代数组中的每一个元素。
# .age:从每个元素中提取 age 字段的值。 这会生成一系列数字流(例如:25, 30, 22)。
# [.[].age] 方括号将前面生成的数字流收集起来,重新组合成一个新的数组。
# add 内置函数
$ jq '[.[].age] | add' users.json
105
# 计算平均年龄
$ jq '[.[].age] | add / length' users.json
35
# 年龄乘以 2
$ jq '.[] | .age * 2' users.json
56
70
84
3. 字符串操作
bash
# 姓名转大写
$ jq '.[] | .name | ascii_upcase' users.json
"ALICE"
"BOB"
"CHARLIE"
# 姓名转小写
$ jq '.[] | .name | ascii_downcase' users.json
"alice"
"bob"
"charlie"
# 字符串连接
$ jq '.[] | "\(.name) is \(.age) years old"' users.json
"Alice is 28 years old"
"Bob is 35 years old"
"Charlie is 42 years old"
高级用法示例
1. 使用变量
bash
# 使用 --arg 传递字符串变量
$ jq --arg city "Chicago" '.[] | select(.address.city == $city)' users.json
{
"id": 3,
"name": "Charlie",
"age": 42,
"email": "charlie@example.com",
"isActive": true,
"address": {
"street": "789 Pine Rd",
"city": "Chicago",
"zip": "60601"
},
"hobbies": [
"cooking",
"traveling"
]
}
# 使用 --argjson 传递 JSON 值 (保持原本的数据类型,如数字、布尔值、数组、对象等)此时传递的是 number 类型的数据
$ jq --argjson min_age 30 '.[] | select(.age >= $min_age)' users.json
# 多个变量 同时支持 and 操作
$ jq --arg city "New York" --argjson min_age 28 \
'.[] | select(.address.city == $city and .age >= $min_age)' users.json
# 结果如下:
{
"id": 1,
"name": "Alice",
"age": 28,
"email": "alice@example.com",
"isActive": true,
"address": {
"street": "123 Main St",
"city": "New York",
"zip": "10001"
},
"hobbies": [
"reading",
"hiking",
"coding"
]
}
2. 修改 JSON 数据
bash
# 添加新字段
$ jq '.[] | .country = "USA"' users.json
# 修改字段值
$ jq '.[] | .age = (.age + 1)' users.json
# 删除字段
$ jq '.[] | del(.email)' users.json
# 更新嵌套字段
$ jq '.[] | .address.zip = "00000"' users.json
3. 分组和排序
bash
# 按年龄排序
$ jq 'sort_by(.age)' users.json
# 按是否活跃分组
$ jq 'group_by(.isActive)' users.json
# 按城市分组并统计人数
$ jq 'group_by(.address.city) | map({city: .[0].address.city, count: length})' users.json
[
{
"city": "Chicago",
"count": 1
},
{
"city": "Los Angeles",
"count": 1
},
{
"city": "New York",
"count": 1
}
]
4. 处理 API 响应
经常与 curl 在一起使用,来提取一些需要关注的数据
bash
# 从 API 获取数据并处理(示例)
$ curl -s https://api.github.com/users/octocat | jq '{name: .name, repo: .public_repos}'
# 处理复杂 API 响应
$ curl -s https://api.example.com/data | \
jq '[.results[] | {id: .id, title: .title}] | sort_by(.id)'
实际应用场景
场景一:日志分析
假设有一个 JSON 格式的日志文件 app.log:
bash
cat > app.log << 'EOF'
{"timestamp": "2026-03-10T10:15:23Z", "level": "error", "msg": "Database connection failed", "service": "api"}
{"timestamp": "2026-03-10T10:16:45Z", "level": "info", "msg": "User logged in", "service": "auth"}
{"timestamp": "2026-03-10T10:17:02Z", "level": "error", "msg": "Timeout exceeded", "service": "api"}
{"timestamp": "2026-03-10T10:18:30Z", "level": "warn", "msg": "High memory usage", "service": "monitor"}
EOF
# 筛选所有错误日志
$ jq -c 'select(.level == "error")' app.log
# 提取错误消息和时间
$ jq -c 'select(.level == "error") | {time: .timestamp, message: .msg}' app.log
# 统计各级别日志数量
$ jq -s 'group_by(.level) | map({level: .[0].level, count: length})' app.log
场景二:配置文件处理
bash
# 创建配置示例
cat > config.json << 'EOF'
{
"database": {
"host": "localhost",
"port": 5432,
"timeout": 30
},
"cache": {
"enabled": true,
"ttl": 3600
}
}
EOF
# 读取配置值
$ jq '.database.host' config.json
"localhost"
# 修改配置值
$ jq '.database.timeout = 60' config.json
# 保存修改后的配置
$ jq '.database.timeout = 60' config.json > temp.json && mv temp.json config.json
场景三:Docker 镜像处理
bash
# 查看 Docker 镜像信息
$ docker images --format '{{.Repository}}:{{.Tag}}' | \
jq -R -s 'split("\n") | map(select(length > 0))'
# 处理 docker inspect 输出
$ docker inspect container_name | jq '.[0].NetworkSettings.IPAddress'
场景四:Kubernetes 资源处理
bash
# 获取所有 Pod 的名称和状态
$ kubectl get pods -o json | \
jq '.items[] | {name: .metadata.name, status: .status.phase}'
# 筛选运行中的 Pod
$ kubectl get pods -o json | \
jq '.items[] | select(.status.phase == "Running") | .metadata.name'
场景五:CSV 格式转换
bash
# 将 JSON 转换为 CSV
$ jq -r '["Name", "Age", "City"],
(.[] | [.name, .age, .address.city]) | @csv' users.json
# 输出:
# "Name","Age","City"
# "Alice",28,"New York"
# "Bob",35,"Los Angeles"
# "Charlie",42,"Chicago"
与其他命令结合使用
1. 与 curl 结合
bash
# 获取 GitHub 用户信息并格式化
$ curl -s https://api.github.com/users/octocat | jq '.name, .public_repos'
# 只获取仓库数量(原始输出)
$ curl -s https://api.github.com/users/octocat | jq -r '.public_repos'
常见问题
Q1: 如何处理大型 JSON 文件?
答: 对于非常大的 JSON 文件,使用流式处理避免一次性加载到内存:
bash
# 使用 inputs 逐个处理
$ jq -n 'inputs | select(.age > 30)' largefile.json
# 或者逐行处理(如果每行是一个完整的 JSON 对象)
$ while read line; do
echo "$line" | jq -c 'select(.isActive)'
done < largefile.json
Q2: 如何处理 JSON 数组外的多个 JSON 对象?
答: 使用 -s (slurp) 选项将多个 JSON 对象读入数组:
bash
# 假设有多个 JSON 对象(不是数组格式)
$ cat multiple.json | jq -s '.'
# 然后像处理数组一样处理
$ cat multiple.json | jq -s '.[] | select(.age > 30)'
Q3: 如何处理包含特殊字符的字符串?
答: 使用 -r 选项输出原始字符串,但要注意转义:
bash
# 包含换行符和引号的字符串 -r 原始字符串
$ echo '{"msg": "Hello\nWorld"}' | jq -r '.msg'
Hello
World
# 如果要保留 JSON 转义
$ echo '{"msg": "Hello\nWorld"}' | jq '.msg'
"Hello\nWorld"
Q4: jq 表达式太复杂怎么办?
答: 可以将复杂的表达式保存到文件中:
bash
# 创建过滤器文件 filter.jq
cat > filter.jq << 'EOF'
.[] |
select(.isActive) |
{
name: .name,
city: .address.city,
hobbyCount: (.hobbies | length)
}
EOF
# 使用 -f 选项加载过滤器 指定jq 查询语法
$ jq -f filter.jq users.json
Q5: 如何处理可能不存在的字段?
答: 使用 ? 操作符或 try-catch:
bash
# 使用 ? 忽略错误
$ jq '.[] | .nonExistentField? // "default"' users.json
# 使用 try-catch
$ jq '.[] | try .unknown_field catch "default value"' users.json
# 使用 // 提供默认值
$ jq '.[] | .nickname // .name' users.json
性能优化技巧
1. 使用 -c 选项减少输出大小
bash
# 紧凑输出更快
$ jq -c '.' largefile.json > compact.json
2. 尽早过滤减少数据处理量
bash
# 先过滤再处理(更高效)
$ jq '.[] | select(.age > 30) | .name' users.json
# 而不是
$ jq '.[] | .name | select(. != null)' users.json
3. 避免不必要的字段
bash
# 只提取需要的字段,提取特定的字段
$ jq '.[] | {name: .name, email: .email}' users.json
总结
jq 是处理 JSON 数据的强大工具,掌握以下核心要点可以让你的工作更高效:
- 基础操作 :
.访问字段,[]访问数组元素 - 常用选项 :
-r原始输出,-c紧凑输出,-sslurp 模式 - 过滤 :
select()条件筛选 - 转换:创建新对象,数学运算,字符串操作
- 变量 :
--arg和--argjson传递参数 - 管道 :
|连接多个操作
合理使用 jq 可以让你的 Shell 脚本在处理 JSON 数据时更加简洁高效!
参考文档
jq 官方手册
jq map-map_values
jq tostring
Linux jq 命令 - 菜鸟教程
分享快乐,留住感动。'2026-03-13 22:15:20' --frank