Linux命令 jq详解

文章目录

    • [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 响应)
      • 实际应用场景
      • 与其他命令结合使用
        • [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 函数

mapmap_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}

注意事项:

  1. contains() vs test():

    • contains(): 简单的字符串包含检查,区分大小写,不支持正则
    • test(): 支持正则表达式,功能更强大
  2. 性能考虑:

    • contains() 通常比 test()
    • 对于简单包含检查,优先使用 contains()
  3. 转义特殊字符:

    • 使用 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 紧凑输出,-s slurp 模式
  • 过滤select() 条件筛选
  • 转换:创建新对象,数学运算,字符串操作
  • 变量--arg--argjson 传递参数
  • 管道| 连接多个操作

合理使用 jq 可以让你的 Shell 脚本在处理 JSON 数据时更加简洁高效!

参考文档

jq 官方手册
jq map-map_values
jq tostring
Linux jq 命令 - 菜鸟教程
分享快乐,留住感动。'2026-03-13 22:15:20' --frank

相关推荐
君生我老3 小时前
Linux 权限
linux
hy____1233 小时前
Linux_网络基础3
linux·服务器·网络
我不听你讲话3 小时前
LNMP网络服务搭建
linux·php·apache
starvapour3 小时前
将Ubuntu设置为不使用独显渲染桌面
linux·ubuntu
火车叼位4 小时前
Docker Compose 网络原理与实战:同一 Compose 服务间如何稳定通信
运维·docker·容器
白狐_7985 小时前
从零构建飞书 × OpenClaw 自动化情报站(三)
运维·自动化·飞书
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ5 小时前
ubuntu 安装部署docker教程
linux·ubuntu·docker
YMWM_5 小时前
安装dora-rs的CLI
linux
人间打气筒(Ada)7 小时前
mysql数据库之DDL、DML
运维·数据库·sql·mysql·dba·dml·dql