jq JSON 处理工具完整指南

jq 学习笔记 -- JSON 命令行处理利器

分类:开发工具

核心主题:jq -- 轻量级、灵活的 JSON 命令行处理工具

主要内容:全面系统地讲解 jq JSON 处理工具,涵盖安装配置、基本查询、过滤器操作、数组与对象处理、管道与函数、高级特性、实战应用场景及与 Claude Code 结合使用。

关键词:jq, JSON, 命令行, JSON处理, 数据查询, 过滤器, JSON解析, 数据处理

目录

  1. jq 概述与基本概念
  2. 安装与配置
  3. 基本查询语法
  4. 过滤器操作详解
  5. 数组与对象处理
  6. 管道与函数
  7. 条件与迭代
  8. 字符串操作
  9. 高级特性
  10. 实战应用场景
  11. 与 Claude Code 结合使用
  12. 常见问题与排错
  13. 核心总结

一、jq 概述与基本概念

1.1 什么是 jq

jq 是一个轻量级、灵活的命令行 JSON 处理工具。它就像 JSON 数据的 grep/sed/awk,让你能够以极其简洁的方式解析、过滤、映射和转换 JSON 数据。jq 使用一种强大的领域特定语言(DSL),通过管道和过滤器组合的方式操作 JSON 数据,支持复杂的查询、变换和格式化输出。

jq 的设计哲学

jq 的设计哲学与 Unix 管道哲学一脉相承:每个过滤器做一件事,通过管道(|)组合成强大的数据处理流水线。jq 的输入是 JSON 数据流,输出也是 JSON 数据流,这使得它可以无缝嵌入到任何 Shell 管道链中。一个典型的 jq 命令就像是一个声明式的数据转换描述:你描述"想要什么",而不是"如何获取"。

1.2 为什么需要 jq

在现代开发和运维中,JSON 已经成为最通用的数据交换格式。API 响应、配置文件、日志记录、数据库导出等场景无处不在。手动使用 grep、sed、awk 等传统文本工具处理结构化 JSON 数据存在诸多痛点:

痛点传统文本工具的问题jq 的解决方案
嵌套结构grep/sed 无法理解 JSON 的嵌套层级关系jq 原生支持通过 . 运算符访问嵌套属性
数组索引awk 很难提取数组中的特定元素jq 支持 .[], .[index], .[start:end] 等数组操作
格式化输出压缩的 JSON 一行难以阅读jq 默认输出美化格式,也可以输出紧凑格式
条件过滤需要在管道中组合多个命令实现过滤jq 的 select() 函数一行实现条件过滤
数据转换复杂的 Shell 脚本才能完成数据重塑jq 通过管道组合过滤器轻松完成数据映射和转换
键值操作提取/修改/删除 JSON key 需要复杂操作jq 支持 del(), map(), with_entries()
核心价值:jq 将 JSON 从原始文本提升为可编程操作的结构化数据。它的学习曲线虽然比 grep 陡峭,但一旦掌握,处理 JSON 的效率可以提升 10 倍以上。对于日常使用 API、处理配置文件、分析日志的开发者来说,jq 是不可或缺的生产力工具。

1.3 jq 的工作模式

jq 的核心工作模式可以概括为三步:

  1. 输入:读取 JSON 数据(从标准输入或文件)
  2. 处理:应用过滤器(filter)对数据进行变换
  3. 输出:输出处理后的 JSON 数据
# jq 命令的基本形式 jq '过滤器' input.json # 或通过管道接收输入 cat input.json | jq '过滤器' # 处理 API 响应 curl -s https://api.example.com/data | jq '.items'
JSON 输入 jq 过滤器 JSON 输出

1.4 标识符:. 的含义

在 jq 中,. 是最基本的过滤器,代表"当前输入"或"整个文档"。它类似于面向对象语言中的 thisself。单独使用 . 时,只是将输入原样输出(通常会对输入进行格式化美化和着色)。

# . 过滤器:输出整个 JSON 文档(格式化) echo '{"name":"Alice","age":30}' | jq '.'
{ "name": "Alice", "age": 30 }

颜色输出

jq 默认开启彩色输出:键名为蓝色、字符串为绿色、数字为紫色、布尔值为黄色、null 值为灰色。这在终端中阅读时非常清晰。如果要禁用颜色,可以使用 --monochrome-output-M 选项。

二、安装与配置

2.1 各平台安装方式

jq 使用 C 语言编写,无运行时依赖,单个二进制文件即可运行。以下是在各主流平台上的安装方法:

平台安装命令说明
Linux (Debian/Ubuntu)sudo apt install jq官方仓库通常提供稳定版本
Linux (RHEL/CentOS/Fedora)sudo yum install jqsudo dnf install jqEPEL 仓库可能需额外启用
Linux (Arch)sudo pacman -S jqArch 社区仓库维护
macOS (Homebrew)brew install jq最推荐的方式,版本较新
macOS (MacPorts)port install jqMacPorts 用户的选择
Windows (Chocolatey)choco install jqWindows 包管理器
Windows (Scoop)scoop install jqScoop 用户的选择
Windows (WSL)sudo apt install jq (WSL 环境内)在 WSL Linux 子系统中安装
Windows (二进制下载)从 jq 官网下载 jq.exe 并加入 PATH无依赖的单个 exe 文件
Dockerdocker pull stedolan/jq容器化运行
Go (源码编译)go install github.com/itchyny/gojq/cmd/gojq@latestGo 语言实现的 jq 替代品
# 验证安装 jq --version # 如果安装成功,会显示版本号,例如: jq-1.7.1 # Windows 下直接下载 jq.exe 并放置于 PATH 目录中 # 下载地址:https://jqlang.github.io/jq/download/ curl -L -o /usr/local/bin/jq https://github.com/jqlang/jq/releases/latest/download/jq-linux-amd64 chmod +x /usr/local/bin/jq

2.2 版本选择

版本特点建议
jq 1.6 (2018)广泛使用的稳定版本,多数系统默认兼容性好,功能完整
jq 1.7 (2023)最新稳定版,新增 --raw-output 改进等推荐升级,向下兼容
gojq (第三方)Go 语言实现,支持 yq 格式互转需要多格式支持时选用

推荐版本

建议始终使用 jq 1.7 或更新版本。jq 1.7 修复了 1.6 中的多个 bug,提升了性能,并且完全向后兼容。你可以通过 jq --version 检查当前版本。如果你的系统自带版本较旧,建议从官方 GitHub Releases 页面下载最新版本。

2.3 基本使用选项

选项简写说明
--raw-output-r输出原始字符串(不带 JSON 引号)
--raw-input-R将输入视为原始字符串而非 JSON
--null-input-n不读取输入,使用 null 作为输入值
--compact-output-c紧凑输出(每行一个 JSON 对象)
--color-output-C强制彩色输出(即使不是 tty)
--monochrome-output-M禁用彩色输出
--arg将 Shell 变量传入 jq 过滤器
--argjson将 JSON 值传入 jq 过滤器
--slurp-s将输入流读入数组后再处理
--from-file-f从文件读取过滤器
# 常用选项示例 # 原始字符串输出(去掉 JSON 引号) echo '{"name":"Alice"}' | jq -r '.name' # 输出: Alice (而不是 "Alice") # 紧凑输出(一行) echo '{"a":1,"b":2}' | jq -c '.' # 输出: {"a":1,"b":2} # 使用 -n 从零开始构建 JSON jq -n '{name: "test", value: 42}' # 输出: {"name":"test","value":42} # 从 stdin 读取多行 JSON 并 slurp 为数组 jq -s '.' file1.json file2.json

三、基本查询语法

3.1 最简单的查询:属性访问

使用点号(.)加属性名即可访问 JSON 对象的键值。这是 jq 最基础和常用的操作。

# 准备一个 JSON 文件 data.json { "name": "Alice", "age": 30, "city": "Shanghai", "job": { "title": "Engineer", "company": "Tech Corp", "salary": 50000 }, "skills": ["Python", "Go", "JavaScript"], "active": true } # 访问根级属性 jq '.name' data.json # "Alice" jq '.age' data.json # 30 jq '.city' data.json # "Shanghai" jq '.active' data.json # true # 访问嵌套属性(方式一:链式点号) jq '.job.title' data.json # "Engineer" jq '.job.company' data.json # "Tech Corp" jq '.job.salary' data.json # 50000 # 访问嵌套属性(方式二:使用 .[] 进行对象迭代) jq '.job | .title' data.json # 等价于 .job.title,通过管道

3.2 可选操作符 ?

当访问可能不存在的属性时,可以使用 ? 操作符避免错误。这在处理动态结构或异构数据时非常有用。

# 如果一个属性不存在,jq 默认返回 null jq '.nonexistent' data.json # null # 但如果在嵌套路径中某个中间级属性为 null 或不存在,会报错 jq '.job.nonexistent.field' data.json # 报错:Cannot index null with string "field" # 使用 ? 可以安全地处理(在 jq 1.7+ 中更稳健) # 通过 select() 和 try-catch 模式 jq 'try .job.nonexistent.field catch null' data.json # 安全返回 null

3.3 多个属性查询

使用逗号 , 分隔多个过滤器,可以同时获取多个属性的值。

# 同时查询多个字段 jq '.name, .age, .city' data.json # 输出: "Alice" 30 "Shanghai" # 使用花括号构建新对象 jq '{name: .name, age: .age}' data.json # 输出: {"name":"Alice","age":30} # 简写形式:使用对象构造器直接提取 jq '{name, age, city}' data.json # 输出: {"name":"Alice","age":30,"city":"Shanghai"} # 提取嵌套字段并重命名 jq '{员工姓名: .name, 职位: .job.title, 公司: .job.company}' data.json # 输出: {"员工姓名":"Alice","职位":"Engineer","公司":"Tech Corp"}

, 与 {} 的区别

使用逗号 , 分隔时,每个过滤器产生独立的输出流(多行输出)。使用花括号 {} 时,将所有字段组合成一个 JSON 对象输出。, 适用于需要逐个处理多个值的场景,{} 适用于需要结构化输出的场景。

3.4 输入输出格式

jq 可以处理两种输入模式:单个 JSON 对象和 JSON Lines(每行一个 JSON 对象)。输出也可以是美化格式或紧凑格式。

# JSON Lines 输入(每行一个 JSON 对象) cat << 'EOF' | jq -c '.name' {"name": "Alice", "id": 1} {"name": "Bob", "id": 2} {"name": "Charlie", "id": 3} EOF # 输出: "Alice" "Bob" "Charlie" # 使用 -c 保持 JSON Lines 输出格式 jq -c '{name, id}' data.json # 输出: {"name":"Alice","id":1}

JSON Lines (NDJSON)

JSON Lines(也称 NDJSON —— Newline Delimited JSON)是一种将多个 JSON 对象放在同一文件中、每行一个的格式。这是很多日志系统和流处理系统的标准输出格式。jq 的 -c 选项正好产生这种格式,非常适合管道链中的进一步处理。

四、过滤器操作详解

4.1 管道过滤器 |

jq 中的管道操作符 | 与 Shell 中的管道概念完全一致:将左侧过滤器的输出作为右侧过滤器的输入。这是构建复杂查询的基础。

# 管道的威力:组合简单过滤器完成复杂操作 jq '.job | .title' data.json # "Engineer"(等价于 .job.title) # 管道与对象构造结合 jq '.job | {title, salary}' data.json # 输出: {"title":"Engineer","salary":50000} # 多层管道 jq '.skills | .[] | ascii_upcase' data.json # 输出: "PYTHON" "GO" "JAVASCRIPT"
理解 jq 管道:jq 中的管道与 Shell 的管道非常相似。每个过滤器接收一个输入值,产生零个或多个输出值。管道将左侧的输出逐个传递给右侧的过滤器。这意味着如果左侧产生了 N 个结果,右侧过滤器会被执行 N 次(每次接收一个输入值)。这种"流式"处理模型是 jq 强大和高效的根源。

4.2 标识过滤器 ...

. 代表当前节点的值。.. 是递归下降操作符,递归遍历 JSON 结构中的所有节点。

# 递归遍历所有节点 jq '..' data.json # 输出 JSON 中每个节点的值(深度优先遍历) # 实战:递归查找所有字符串值 jq '.. | strings' data.json # 输出: "Alice", "Shanghai", "Engineer", "Tech Corp", "Python", ... # 递归查找所有数字值 jq '.. | numbers' data.json # 输出: 30, 50000 # 递归查找特定键 jq '.. | .name? // empty' data.json # 输出: "Alice" # 递归查找所有布尔值 jq '.. | booleans' data.json # 输出: true

4.3 类型过滤器

jq 提供了类型过滤器,用于根据值的类型进行过滤。

过滤器描述示例
strings只保留字符串值.[] | strings
numbers只保留数字值.[] | numbers
booleans只保留布尔值.[] | booleans
nulls只保留 null 值.[] | nulls
arrays只保留数组.[] | arrays
objects只保留对象.[] | objects
iterables保留数组和对象.[] | iterables
scalars保留标量值(非数组非对象).[] | scalars
# 类型过滤器示例 jq -n '[1, "hello", null, true, {}, []] | .[] | type' # 输出: "number", "string", "null", "boolean", "object", "array" jq -n '[1, "hello", null, true, {}, []] | .[] | scalars' # 输出: 1, "hello", null, true

4.4 表达式过滤器 ?//

jq 提供了错误抑制操作符 ? 和默认值操作符 //,用于处理缺失值或错误。

# // 操作符:提供默认值 echo '{"name": "Alice"}' | jq '.age // 0' # 输出: 0 (因为 .age 不存在,结果为 null,// 提供了默认值 0) echo '{"name": "Alice", "age": 25}' | jq '.age // 0' # 输出: 25(因为 .age 存在且不为 null) # 实用模式:使用 // 设置嵌套属性的默认值 jq '(.job.salary // 0) * 12' data.json # 输出: 600000(年薪) # 使用 empty 跳过不存在的值 jq 'try .job.nonexistent catch empty' data.json # 无输出(跳过该值)

五、数组与对象处理

5.1 数组迭代 .[]

.[] 是数组迭代操作符,它将数组中的每个元素展开为独立的输出值。这是 jq 中最常用的操作符之一。

# 准备数组数据 echo '{"items": ["apple", "banana", "cherry"]}' | jq '.items[]' # 输出: "apple" "banana" "cherry" # 迭代对象数组 echo '[{"name":"Alice","age":30},{"name":"Bob","age":25},{"name":"Charlie","age":35}]' | jq '.[]' # 输出三个对象 # 结合管道:迭代并提取字段 echo '[{"name":"Alice","age":30},{"name":"Bob","age":25}]' | jq '.[] | .name' # 输出: "Alice" "Bob" # 使用花括号构造新对象 echo '[{"name":"Alice","age":30},{"name":"Bob","age":25}]' | jq '.[] | {name, age}'

5.2 数组切片 .[start:end]

jq 支持与 Python 一致的数组切片语法,可以快速获取数组的子集。

# 数组切片 jq -n '[10, 20, 30, 40, 50] | .[0:2]' # 输出: [10, 20] jq -n '[10, 20, 30, 40, 50] | .[2:]' # 输出: [30, 40, 50] jq -n '[10, 20, 30, 40, 50] | .[:-2]' # 输出: [10, 20, 30] jq -n '[10, 20, 30, 40, 50] | .[-2:]' # 输出: [40, 50] jq -n '[10, 20, 30, 40, 50] | .[1:4]' # 输出: [20, 30, 40]

切片规则

  • .[m:n] -- 从索引 m 到 n(不包含 n),与 Python 一致
  • .[n:] -- 从索引 n 到末尾
  • .[:n] -- 从开头到索引 n(不包含 n)
  • .[-n:] -- 最后 n 个元素
  • .[:-n] -- 去掉最后 n 个元素后的部分

5.3 数组构造 []

使用方括号可以将多个输出值收集为数组。

# 将多个值收集为数组 jq '[.name, .age, .city]' data.json # 输出: ["Alice", 30, "Shanghai"] # 将迭代结果收集为数组 echo '[{"name":"Alice"},{"name":"Bob"},{"name":"Charlie"}]' | jq '[.[] | .name]' # 输出: ["Alice","Bob","Charlie"] # 等价写法:使用 map 函数 echo '[{"name":"Alice"},{"name":"Bob"},{"name":"Charlie"}]' | jq 'map(.name)' # 输出: ["Alice","Bob","Charlie"]

5.4 对象构造与解构

jq 提供了灵活的对象构造语法,可以轻松创建新对象或重塑现有对象。

# 对象构造器 jq '{full_name: .name, age_years: .age, job_title: .job.title}' data.json # 输出: {"full_name":"Alice","age_years":30,"job_title":"Engineer"} # 使用简写形式(键名与属性名相同时) jq '{name, age, job}' data.json # 输出: {"name":"Alice","age":30,"job":{"title":"Engineer","company":"Tech Corp","salary":50000}} # 添加新字段 jq '. + {annual_salary: (.job.salary * 12)}' data.json # 在原对象上添加 annual_salary 字段 # 合并多个对象 jq -n '{a: 1, b: 2} + {b: 3, c: 4}' # 输出: {"a":1,"b":3,"c":4} (b 的值被覆盖) # 多表达式对象构造 jq '{name, (": " + (.age | tostring)): .age}' data.json # 动态键名

5.5 删除字段 del()

# 删除单个字段 jq 'del(.job.salary)' data.json # 返回不包含 salary 的新对象 # 删除多个字段 jq 'del(.job, .skills)' data.json # 返回只包含 name, age, city, active 的新对象 # 条件删除:删除值为空的字段 jq 'with_entries(select(.value != null and .value != ""))' data.json

5.6 对象字段迭代 to_entries / from_entries / with_entries

这三个函数是处理对象键值对的瑞士军刀。它们将对象转换为键值对数组,操作后再转回对象。

# to_entries:对象 → [{key, value}] 数组 jq -n '{a: 1, b: 2, c: 3} | to_entries' # 输出: [{"key":"a","value":1},{"key":"b","value":2},{"key":"c","value":3}] # from_entries:[{key, value}] 数组 → 对象 jq -n '[{"key":"a","value":1},{"key":"b","value":2}] | from_entries' # 输出: {"a":1,"b":2} # with_entries:组合操作,等价于 to_entries | map(filter) | from_entries # 过滤:只保留数值大于 1 的字段 jq -n '{a: 1, b: 2, c: 3, d: 4} | with_entries(select(.value > 1))' # 输出: {"b":2,"c":3,"d":4} # 转换:将所有值翻倍 jq -n '{a: 1, b: 2, c: 3} | with_entries(.value *= 2)' # 输出: {"a":2,"b":4,"c":6} # 重命名键 jq -n '{old_name: 1} | with_entries(.key = "new_name")' # 输出: {"new_name":1}

六、管道与函数

6.1 数学函数

jq 内置了丰富的数学函数,支持对数值进行各种运算。

# 基本数学运算 jq -n '[1, 2, 3, 4, 5] | add' # 15(求和) jq -n '[1, 2, 3, 4, 5] | length' # 5(数组长度) jq -n '[1, 2, 3, 4, 5] | min' # 1 jq -n '[1, 2, 3, 4, 5] | max' # 5 jq -n '[1, 2, 3, 4, 5] | min_by(.)' # 1 jq -n '[1, 2, 3, 4, 5] | max_by(.)' # 5 jq -n '[1, 2, 3, 4, 5] | sort' # [1,2,3,4,5] jq -n '[1, 2, 3, 4, 5] | reverse' # [5,4,3,2,1] jq -n '[1, 2, 3, 4, 5] | average' # 3

6.2 字符串函数

# 字符串拼接和转换 jq -n '"hello" + " " + "world"' # "hello world" jq -n '42 | tostring' # "42" jq -n '"42" | tonumber' # 42 jq -n '"hello" | length' # 5 jq -n '"hello" | ascii_upcase' # "HELLO" jq -n '"HELLO" | ascii_downcase' # "hello" jq -n '" hello " | ltrimstr(" ")' # 移除前导空格(简单方式) jq -n '"hello\nworld"' # 字符串中的转义字符 # 字符串分割和合并 jq -n '"a,b,c" | split(",")' # ["a","b","c"] jq -n '["a","b","c"] | join(",")' # "a,b,c" # 字符串截取 jq -n '"hello"[0:2]' # "he" jq -n '"hello"[2:]' # "llo" jq -n '"hello"[:4]' # "hell" # 字符串包含和匹配 jq -n '"hello world" | contains("world")' # true jq -n '"hello" | startswith("he")' # true jq -n '"hello" | endswith("lo")' # true jq -n '"hello" | index("el")' # 1(索引位置)

6.3 数组函数

# map:对每个元素应用过滤器 jq -n '[1, 2, 3, 4] | map(. * 2)' # [2,4,6,8] jq -n '[1, 2, 3, 4] | map(select(. > 2))' # [3,4] # map_values:对对象的值应用过滤器 jq -n '{a: 1, b: 2, c: 3} | map_values(. * 2)' # {"a":2,"b":4,"c":6} # unique:去重并排序 jq -n '[3, 1, 2, 1, 3, 2] | unique' # [1,2,3] # group_by:分组(需要先排序) jq -n '[{"city":"SH","val":1},{"city":"BJ","val":2},{"city":"SH","val":3}] | group_by(.city)' # 输出: [[{"city":"BJ","val":2}],[{"city":"SH","val":1},{"city":"SH","val":3}]] # flatten:展平嵌套数组 jq -n '[[1,2],[3,[4,5]]] | flatten' # [1,2,3,4,5] jq -n '[[1,2],[3,[4,5]]] | flatten(1)' # [1,2,3,[4,5]](只展平一层)
map 与 .[] 的区别:map(f) 等价于 [.[] | f]。前者保持数组结构,后者展开为独立值。当需要保留数组结构时使用 map,当需要逐个处理时使用 .[]。这是一个重要的概念区别。

6.4 选择函数 select()

select() 是 jq 中最常用的条件过滤函数。它接收一个条件表达式,只保留满足条件的值。

# 准备数据 echo '[{"name":"Alice","age":30,"salary":50000},{"name":"Bob","age":25,"salary":40000},{"name":"Charlie","age":35,"salary":60000}]' | \ jq '.[] | select(.age > 28)' # 输出年龄大于 28 的人 # 复合条件 jq '.[] | select(.age > 25 and .salary > 45000)' # 输出年龄大于 25 且薪资大于 45000 的人 # 字符串匹配 jq '.[] | select(.name | startswith("A"))' # 输出名字以 A 开头的人 # 使用 contains 模糊匹配 jq '.[] | select(.name | contains("li"))' # 输出名字包含 "li" 的人(不区分大小写需要 test 函数) # 使用 test 进行正则匹配 jq '.[] | select(.name | test("^[AB]"))' # 输出名字以 A 或 B 开头的人 # 字段存在性检查 jq '.[] | select(has("email"))' # 输出包含 email 字段的对象 # 否定条件 jq '.[] | select(.age < 30 | not)' # 输出年龄不小于 30 的人

6.5 排序函数 sort_by / group_by

# sort_by:按字段排序 jq 'sort_by(.age)' users.json # 按年龄升序排列 jq 'sort_by(-.age)' users.json # 按年龄降序排列(取负值) jq 'sort_by(.city, .name)' users.json # 先按城市排序,再按姓名排序 # group_by + 聚合:按部门统计人数 jq 'group_by(.dept) | map({dept: .[0].dept, count: length, total_salary: map(.salary) | add})' employees.json

七、条件与迭代

7.1 if-then-else

jq 支持完整的条件表达式语法。

# 基本 if 语句 jq 'if .age >= 18 then "adult" else "minor" end' data.json # 输出: "adult" # if-elif-else jq 'if .age < 18 then "minor" elif .age < 60 then "adult" else "senior" end' data.json # 输出: "adult" # 在 map 中使用 if echo '[25, 16, 35, 12, 60]' | jq 'map(if . >= 18 then "Y" else "N" end)' # 输出: ["Y","N","Y","N","Y"] # 在 select 中使用 if echo '[1,2,3,4,5]' | jq '.[] | select(if . > 3 then true else false end)' # 输出: 4, 5

7.2 比较操作符

操作符含义示例结果
==相等比较1 == 1true
!=不等比较1 != 2true
>大于3 > 2true
>=大于等于3 >= 3true
<小于2 < 3true
<=小于等于2 <= 2true
and逻辑与true and falsefalse
or逻辑或true or falsetrue
not逻辑非not truefalse
# 比较操作的实用模式 # 范围检查 jq 'select(.age >= 20 and .age <= 40)' # 年龄在 20-40 之间 # null 检查 jq 'select(.email != null)' # 有邮箱地址的记录 # 使用 alternative 操作符进行选择 jq 'select(.role == "admin" or .role == "moderator")'

7.3 迭代模式详解

# 模式 1:遍历并收集 jq '[.[] | {name, age}]' users.json # 收集所有用户的 name 和 age # 模式 2:遍历并过滤 jq '[.[] | select(.active == true) | {name}]' users.json # 获取所有活跃用户的名称 # 模式 3:遍历并转换 jq '[.[] | {name, age_group: if .age >= 60 then "senior" elif .age >= 18 then "adult" else "minor" end}]' users.json # 模式 4:嵌套遍历 jq '.[] | .orders[] | {user: .user, product: .product}' data.json # 遍历用户及其订单

性能注意事项

在处理大型 JSON 文件(如数百 MB 的日志)时,jq 的流式处理模型是高效的。但要注意:map 和数组构造器 [] 会将整个结果加载到内存中。如果数据量极大,应该避免使用数组构造器,而是使用 .[] 逐行输出并在下游处理。

7.4 空值处理 empty

emptynull 是 jq 中两个重要的概念。empty 代表"没有输出"(零个结果),而 null 是一个 JSON 值。

# empty 与 null 的区别 jq -n 'null' # 输出 null jq -n 'empty' # 无输出(零个结果) # 使用 empty 跳过特定元素 jq -n '[1, 2, 3, 4, 5] | .[] | if . > 3 then . else empty end' # 输出: 4, 5(1,2,3 被跳过) # 在 select 中使用 empty 的等价写法 jq -n '[1, 2, 3, 4, 5] | .[] | select(. > 3)' # 输出: 4, 5(效果相同)

八、字符串操作

8.1 字符串插值与格式化

# 字符串拼接 jq '"User: " + .name + ", Age: " + (.age | tostring)' data.json # 输出: "User: Alice, Age: 30" # 使用 \() 字符串插值(jq 1.7 推荐方式) jq '"User: \(.name), Age: \(.age)"' data.json # 输出: "User: Alice, Age: 30" # 在字符串中直接嵌入表达式 jq '"\(.name) works as a \(.job.title) at \(.job.company)"' data.json # 输出: "Alice works as a Engineer at Tech Corp"

字符串插值 vs 拼接

使用 \(expression) 语法(jq 1.7+ 支持)比使用 + 拼接更简洁,且会自动将值转换为字符串,无需手动调用 tostring。推荐在所有字符串格式化场景中使用插值语法。

8.2 正则表达式

jq 提供了完整的正则表达式支持,使用 PCRE(Perl Compatible Regular Expressions)语法。

# test:检查是否匹配(返回 true/false) jq -n '"hello123" | test("^[a-z]+[0-9]+$")' # true jq -n '"Hello123" | test("^[a-z]+[0-9]+$")' # false(大小写敏感) jq -n '"Hello123" | test("^[a-z]+[0-9]+$"; "i")' # true(忽略大小写) # match:返回匹配详情(位置、长度、捕获组) jq -n '"color: #ff8800" | match("#[0-9a-fA-F]+")' # 输出: {"offset":7,"length":7,"string":"#ff8800","captures":[]} # capture:提取命名捕获组 jq -n '"2026-05-08" | capture("(?[0-9]{4})-(?[0-9]{2})-(?[0-9]{2})")' # 输出: {"year":"2026","month":"05","day":"08"} # 使用正则进行条件过滤 jq '.[] | select(.email | test("^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$"))' users.json # 过滤出有效邮箱的用户 # 正则替换 jq -n '"hello world" | sub("world"; "jq")' # "hello jq"(只替换第一个) jq -n '"aaa bbb ccc" | gsub(" "; ",")' # "aaa,bbb,ccc"(全部替换) jq -n '"hello123world" | gsub("[0-9]"; "_")' # "hello___world"

8.3 分割与合并

# split:将字符串分割为数组 jq -n '"a,b,c,d" | split(",")' # ["a","b","c","d"] jq -n '"a|b|c" | split("[|]"; "g")' # ["a","b","c"](正则分割) # join:将数组合并为字符串 jq -n '["a","b","c"] | join(", ")' # "a, b, c" # 实战:解析 CSV 行 echo '"name,age,city" | split(",")' | jq -r '.[]' # 输出: name age city # 实战:将 JSON 数组转换为 TSV echo '[{"name":"Alice","age":30},{"name":"Bob","age":25}]' | \ jq -r '.[] | [.name, (.age | tostring)] | @tsv' # 输出: Alice 30 # Bob 25

8.4 格式化输出指示器

jq 提供了一组特殊的 @ 指令,用于将 JSON 数据转换为其他文本格式。

指令描述示例输入输出
@text字符串文本输出"hello"hello
@jsonJSON 输出{a:1}{"a":1}
@htmlHTML 转义"
"
<br>
@uriURI 编码"a b"a%20b
@csvCSV 格式["a","b"]"a","b"
@tsvTSV 格式["a","b"]a\tb
@shShell 转义"a'b"'a'\''b'
@base64Base64 编码"hello"aGVsbG8=
@base64dBase64 解码"aGVsbG8=""hello"
# CSV 输出示例 echo '[{"name":"Alice","age":30},{"name":"Bob","age":25}]' | \ jq -r '.[] | [.name, .age] | @csv' # 输出: "Alice",30 "Bob",25 # Shell 转义(安全地将值传递给 Shell) jq -r '.[] | [.name, .city] | @sh' data.json # 输出: 'Alice' 'Shanghai' # HTML 转义 jq -n '"<script>alert(1)</script>" | @html' # 输出: <script>alert(1)</script>
@csv 和 @tsv 的注意点:这两个格式化指令要求输入必须是数组。如果输入不是数组,需要用 [...] 包装。配合 -r(raw output)使用才能得到正确的无引号输出。

九、高级特性(reduce、foreach、递归等)

9.1 reduce 函数

reduce 是 jq 中最强大的函数之一,它将数组中的元素逐个累积计算,最终产生一个单一值。这个概念类似于函数式编程中的 fold/reduce/inject。

# reduce 语法:reduce .[] as $item (初始值; 累积表达式) # 示例:计算数组总和 jq -n 'reduce [1,2,3,4,5][] as $n (0; . + $n)' # 输出: 15 # 示例:构建对象 jq -n 'reduce ["a","b","c"][] as $k ({}; .[$k] = 1)' # 输出: {"a":1,"b":1,"c":1} # 复杂示例:按部门汇总薪资 echo '[{"dept":"IT","name":"Alice","salary":5000},{"dept":"IT","name":"Bob","salary":4000},{"dept":"HR","name":"Charlie","salary":3000}]' | \ jq 'reduce .[] as $e ({}; .[$e.dept] += $e.salary)' # 输出: {"IT":9000,"HR":3000} # reduce 配合对象构造:复杂聚合 jq -n '[1,2,3,4,5] | reduce .[] as $n ({sum:0, product:1, count:0}; {sum: .sum + $n, product: .product * $n, count: .count + 1})' # 输出: {"sum":15,"product":120,"count":5}

reduce 执行流程

1. 初始值 0 作为第一个 . 进入累积表达式
2. 数组的第一个元素 $n=1,计算结果 0+1=1,成为新的 .
3. 第二个元素 $n=2,计算结果 1+2=3,成为新的 .
4. 以此类推直到所有元素处理完毕
5. 返回最终累积值

9.2 foreach 函数

foreach 类似于 reduce,但它在每次迭代时都会输出中间结果。这使得它非常适合处理需要累积上下文但又要输出每一步结果的场景。

# foreach 语法:foreach .[] as $item (初始值; 每次迭代更新; 输出表达式) # 示例:计算运行总和并输出每一步 jq -n '[1,2,3,4,5] | foreach .[] as $n (0; . + $n; {step: $n, running_total: .})' # 输出: {"step":1,"running_total":1} {"step":2,"running_total":3} {"step":3,"running_total":6} {"step":4,"running_total":10} {"step":5,"running_total":15} # 另一种输出形式:只输出累积值 jq -n '[1,2,3,4,5] | foreach .[] as $n (0; . + $n; .)' # 输出: 1, 3, 6, 10, 15

9.3 递归 ..recurse()

除了 .. 操作符外,jq 还提供了 recurse() 函数用于自定义递归。

# 使用 .. 递归遍历 jq '.. | select(. != null)' data.json # 输出所有非空节点 # recurse 的基本用法:递归遍历子节点 jq -n '{"a":{"b":{"c":1}}} | recurse | select(type == "object")' # recurse 自定义深度 jq -n '[1,[2,[3,[4]]]] | recurse(.[]?; . | length > 0)' # walk:递归修改整个 JSON 结构 jq -n '{"a":{"b":1,"c":[{"d":2}]},"e":3} | walk(if type == "number" then . * 2 else . end)' # 输出: {"a":{"b":2,"c":[{"d":4}]},"e":6} # walk 函数递归遍历所有节点,对每个节点应用过滤器

9.4 变量绑定 as

as 用于将表达式的结果绑定到变量,在后续过滤器中重复使用。

# 基本变量绑定 jq '.job.salary as $s | {name, annual_salary: ($s * 12)}' data.json # 输出: {"name":"Alice","annual_salary":600000} # 解构绑定:将数组元素绑定到变量 echo '["Alice", 30, "Shanghai"]' | jq '.[0] as $name | .[1] as $age | "\($name) is \($age) years old"' # 输出: "Alice is 30 years old" # 对象解构绑定 jq '{name: $n, age: $a} = . | "\($n) is \($a)"' data.json # 输出: "Alice is 30" # 跨管道共享数据(使用变量避免重复计算) jq '.job.salary as $s | {name, monthly: $s, annual: ($s * 12), tax: ($s * 12 * 0.2)}' data.json

9.5 定义函数 def

jq 允许你定义自己的函数来封装重复逻辑。

# 定义并立即使用函数 jq -n 'def double: . * 2; [1,2,3] | map(double)' # 输出: [2,4,6] # 带参数的函数 jq -n 'def greet(name): "Hello, \(name)!"; greet("World")' # 输出: "Hello, World!" # 实用函数:统计数组中的键值对数量 jq -n 'def count_by(f): reduce .[] as $x ({}; .[$x|f] += 1); ["a","b","a","c","b","b"] | count_by(.)' # 输出: {"a":2,"b":3,"c":1} # 从文件加载函数(jq 1.7 支持的模块系统) # 创建 mylib.jq 文件: # def double: . * 2; # def triple: . * 3; # 然后使用: # jq -L . 'include "mylib"; [1,2,3] | map(double)' data.json

模块系统注意事项

jq 1.7 引入了模块系统(includemodule),但不同版本的 jq 对模块路径的解析规则可能有细微差异。在团队协作中,建议确保所有人使用相同的 jq 版本,或者将常用函数直接写在过滤器中而不是依赖模块文件。

9.6 输入函数 inputs

inputs 函数用于逐行读取输入流,与 -n 选项结合使用可以精细控制处理过程。

# 逐行处理 JSON Lines 输入 echo '{"id":1} {"id":2} {"id":3}' | jq -n 'inputs | .id' # 输出: 1, 2, 3 # 使用 inputs 跳过特定数量的输入 echo '{"id":1} {"id":2} {"id":3}' | jq -n 'first(inputs) | .id' # 输出: 1(只处理第一个) # 配合 foreach 处理连续输入 jq -n 'foreach inputs as $item (0; . + 1; {index: ., value: $item})' < data.jsonl

inputs 与默认输入的区别

默认情况下(不加 -n),jq 将整个输入解析后传递给过滤器。使用 -n + inputs 时,jq 不预先读取输入,而是通过 inputs 函数惰性地逐行读取。这在处理超大文件时特别有用,可以减少内存占用。当需要对数据流进行细粒度控制时(如跳过前 N 行),inputs 是必要的工具。

十、实战应用场景

10.1 API 数据处理

在现代开发中,jq 最常见的用途之一是处理和转换 API 响应数据。无论是 REST API 还是 GraphQL 响应,jq 都能快速提取需要的信息。

# 场景 1:从 GitHub API 获取仓库信息 curl -s https://api.github.com/repos/jqlang/jq | jq '{name, description, stars: .stargazers_count, forks, license: .license.spdx_id}' # 场景 2:提取分页 API 响应中的数据 curl -s 'https://api.github.com/repos/jqlang/jq/issues?state=open&per_page=5' | \ jq '.[] | {number, title, state, user: .user.login, comments}' # 场景 3:处理嵌套 API 响应 curl -s https://api.example.com/v2/users \ | jq '{total: .meta.total, users: [.data[] | {id, name, email, created_at: .created_at[0:10]}]}' # 场景 4:API 健康检查汇总 curl -s https://status.example.com/api/checks \ | jq '[.checks[] | {name, status, response_time: .response_time_ms}] | group_by(.status) | map({status: .[0].status, count: length})'
API 处理最佳实践:
  • 先用 jq '.' 查看 API 响应的整体结构
  • 逐步构建过滤器,从小处着手、逐步扩展
  • 使用 --arg 将 Shell 变量传入过滤器,避免硬编码
  • 对于分页响应,结合 Shell 循环和 jq 完成批量提取

10.2 日志分析

# 场景 1:分析 JSON 格式的应用日志 # 日志格式:{"timestamp":"2026-05-08T10:30:00Z","level":"ERROR","message":"Connection timeout","service":"api","duration_ms":5023} # 提取所有 ERROR 级别日志 jq 'select(.level == "ERROR")' app.log # 统计各服务级别的日志数量 jq -n 'reduce inputs as $log ({}; .[$log.service] += 1)' < app.log # 找出响应时间超过 1 秒的请求 jq 'select(.duration_ms > 1000) | {timestamp, message, duration_ms}' app.log # 按小时统计错误数量 jq -n '[inputs | select(.level == "ERROR") | .timestamp[0:13]] | group_by(.) | map({hour: .[0], count: length})' < app.log # 找出最慢的 10 个请求 jq -s 'sort_by(.duration_ms) | reverse[:10] | [.[] | {timestamp, service, duration_ms}]' app.log
# 场景 2:Nginx/其他访问日志分析(假设日志为 JSON Lines) # 格式: {"ip":"192.168.1.1","method":"GET","path":"/api/users","status":200,"bytes":1234,"response_time":0.345} # 统计 HTTP 状态码分布 jq -n 'reduce inputs as $log ({}; .[$log.status | tostring] += 1)' < access.log # TOP 10 最慢的路径 jq -s 'group_by(.path) | map({path: .[0].path, avg_time: (map(.response_time) | add / length), count: length}) | sort_by(-.avg_time) | .[:10]' access.log # 统计来源 IP 的请求数 jq -n 'reduce inputs as $log ({}; .[$log.ip] += 1) | to_entries | sort_by(-.value) | .[:10]' < access.log

10.3 配置文件处理

# JSON 配置文件管理 # 读取配置并验证 jq 'has("database") and has("server") and .server.port > 0' config.json # 合并多个配置文件(后者的值覆盖前者) jq -s '.[0] * .[1]' base.json override.json > merged.json # 更新配置中的特定字段 jq '.server.port = 8080 | .logging.level = "debug"' config.json > config.new.json # 环境特定的配置替换 jq '.database = (.database | .host = $host | .password = $pwd)' \ --arg host "prod-db.example.com" \ --arg pwd "${DB_PASSWORD}" \ config.json > config.prod.json # 从配置中提取特定模块的配置 jq '{module_name: .name, dependencies: .dependencies | keys, scripts: .scripts | to_entries | map("\(.key): \(.value)")}' package.json

10.4 数据迁移与转换

# 场景 1:JSON 字段重命名 jq '{first_name: .name, age_years: .age, location: .city}' data.json # 场景 2:扁平化嵌套结构 echo '{"user":{"name":"Alice","address":{"city":"SH","zip":"200000"}}}' | \ jq '{user_name: .user.name, user_city: .user.address.city, user_zip: .user.address.zip}' # 场景 3:数组展开(一对多关系) echo '{"orders": [{"id":1,"items":["a","b"]},{"id":2,"items":["c"]}]}' | \ jq '[.orders[] | .items[] as $item | {order_id: .id, item: $item}]' # 输出: [{"order_id":1,"item":"a"},{"order_id":1,"item":"b"},{"order_id":2,"item":"c"}] # 场景 4:数据校验和清洗 jq 'map(select(.email != null and .email | test("@"))) | map({name, email, active: (. active // false)})' users.json # 场景 5:JSON 转换为其他格式(配合 Shell) # JSON to CSV jq -r '.[] | [.name, .age, .city] | @csv' data.json > data.csv # JSON to Markdown 表格 jq -r '.[] | "| \(.name) | \(.age) | \(.city) |"' data.json # 配合表头输出为 Markdown 表格

10.5 与 curl 结合的最佳实践

# 认证 API 请求 + jq 处理 curl -s -H "Authorization: Bearer $TOKEN" https://api.example.com/data \ | jq '.data[] | {id, name, status}' # 使用 jq 构造请求体并发送 POST 请求 payload=$(jq -n '{"name": "New Item", "price": 29.99, "category": "books"}') curl -s -X POST -H "Content-Type: application/json" -d "$payload" https://api.example.com/items \ | jq '.' # 从 API 响应中提取数据并作为下一个请求的参数 item_id=$(curl -s https://api.example.com/items | jq -r '.items[0].id') curl -s "https://api.example.com/items/$item_id/details" | jq '.' # 批量处理多个 API 端点 for endpoint in users orders products; do curl -s "https://api.example.com/$endpoint" | jq '{type: "$endpoint", count: (.meta.total // length)}' done

10.6 Docker 和 Kubernetes 场景

# Docker:查看镜像信息 docker inspect nginx | jq '.[] | {id: .Id[0:12], created: .Created, ports: .Config.ExposedPorts}' # Docker:查看容器资源使用 docker stats --no-stream --format '{{json .}}' | jq -s '.[] | {name: .Name, cpu: .CPUPerc, mem: .MemPerc}' # Kubernetes:提取 Pod 状态 kubectl get pods -o json | jq '.items[] | {name: .metadata.name, status: .status.phase, node: .spec.nodeName, ip: .status.podIP}' # Kubernetes:查看所有容器的镜像版本 kubectl get pods -o json | jq '[.items[] | {name: .metadata.name, containers: [.spec.containers[] | {name, image}]}]' # Kubernetes:检查 deployment 副本状态 kubectl get deployment -o json | jq '.items[] | {name: .metadata.name, replicas: .spec.replicas, ready: .status.readyReplicas // 0, updated: .status.updatedReplicas // 0}'

十一、与 Claude Code 结合使用

11.1 在 Claude Code 中调用 jq

Claude Code 通过 Bash 工具执行命令,jq 是 Claude Code 中进行 JSON 数据处理的天然选择。以下是在 Claude Code 对话中常见的 jq 使用模式:

# 格式化并分析 JSON 文件 jq '.' package.json # 提取 package.json 中的依赖信息 jq '{dependencies: .dependencies | keys, devDependencies: .devDependencies | keys}' package.json # 检查 TypeScript 编译配置 jq '{compilerOptions: .compilerOptions | {target, module, strict, outDir}}' tsconfig.json # 分析测试结果(Jest JSON 输出) jq '{total: .numTotalTests, passed: .numPassedTests, failed: .numFailedTests, time: .testResults[0].duration}' test-results.json # 查看 ESLint 配置规则数量 jq '{rules_count: (.rules | length), extends: .extends}' .eslintrc.json

11.2 与文件操作工具配合

# 使用 jq 处理 Read 工具获取的 JSON 内容 # Claude Code 会先 Read 文件,然后将内容通过管道传给 jq # 查看项目中的所有 JSON 配置文件 find . -name "*.json" -not -path "*/node_modules/*" | xargs jq '.' 2>/dev/null | head -50 # 统计项目中的 JSON 文件大小 find . -name "*.json" -not -path "*/node_modules/*" -exec wc -c {} \; | sort -rn | head -10 # 搜索特定结构的 JSON 文件 for f in $(find . -name "*.json" -not -path "*/node_modules/*"); do if jq -e '.dependencies | has("react")' "$f" >/dev/null 2>&1; then echo "$f" fi done

11.3 在开发工作流中的典型应用

# 在 Claude Code 中分析 API 变更 # 对比两个 API 响应结构的差异 diff <(curl -s https://api-v1.example.com/users | jq '.[] | {id, name}') \ <(curl -s https://api-v2.example.com/users | jq '.[] | {id, name, email}') # 分析 CI/CD Pipeline 配置(GitLab CI) jq '[.jobs[] | {name, stage, image}]' pipeline-report.json # 验证 JSON Schema 兼容性 jq 'has("$schema") and has("properties") and (.properties | length > 0)' schema.json # 分析编译产物 jq '[.outputs[] | {name: .name, size: .size, chunks: .chunks | length}] | sort_by(-.size)' build-stats.json
Claude Code + jq 的效率模式:在 Claude Code 的对话中,当你需要处理 JSON 数据时,可以直接用自然语言描述需求(如"查看这个 package.json 中所有依赖的名称和版本"),而不需要手动编写 jq 过滤器。Claude Code 会自动选择并构建合适的 jq 命令来完成你的需求。这极大降低了 jq 的使用门槛。

11.4 实用组合模式

# 模式 1:提取 + 统计 # 找出项目中所有使用 TypeScript 的 package for pkg in packages/*/package.json; do jq -r 'select(.devDependencies.typescript != null) | .name' "$pkg" done # 模式 2:过滤 + 转换 + 输出 # 将所有 Markdown 文件中的 frontmatter 提取为 JSON for f in *.md; do # 提取 --- 之间的 YAML/JSON frontmatter 并用 jq 分析 sed -n '/^---$/,/^---$/p' "$f" | sed '1d;$d' | jq '.' 2>/dev/null || true done # 模式 3:并行 + jq 分析 # 并行分析多个 API 端点 for ep in users posts comments; do (curl -s "https://api.example.com/$ep" | jq 'length') & done wait

十二、常见问题与排错

12.1 语法和解析错误

错误信息原因解决方案
parse error: Invalid numeric literal at line 1, column 2输入不是合法的 JSON检查 JSON 格式(引号、逗号等),使用 jq . 验证
jq: error: syntax error, unexpected ...过滤器语法错误检查引号、括号配对,注意转义字符
Cannot index array with string ...尝试用字符串索引数组确认数据类型,使用 .[] 或数字索引
Cannot index object with number ...尝试用数字索引对象确认数据类型,使用字符串键名
jq: error (at ...): number (N) cannot be iterated over对非数组值使用 .[]确认值为数组类型,或使用类型过滤器
# 验证 JSON 是否合法 jq '.' file.json > /dev/null 2>&1 || echo "无效的 JSON" # 使用 -e 选项在脚本中检查 JSON 合法性 if jq -e '.' file.json >/dev/null 2>&1; then echo "JSON 有效" else echo "JSON 无效" fi # 非标准 JSON 的处理方法 # 1. 单引号替代双引号 echo "{'key': 'value'}" | sed "s/'/\"/g" | jq '.' # 2. 尾逗号(Trailing comma) echo '{"a":1,"b":2,}' | jq '.' # jq 默认不允许尾逗号 # 使用 Python 的 json 模块预处理 echo '{"a":1,"b":2,}' | python -c "import sys,json; print(json.dumps(json.load(sys.stdin)))" | jq '.'

Shell 引号陷阱

在 Shell 中编写 jq 过滤器时,引号是最容易出错的地方:

  • 使用单引号包裹整个过滤器:jq '.key' file(推荐,避免 Shell 展开)
  • 过滤器中需要单引号时:使用双引号包裹整个过滤器,或转义
  • 过滤器中需要双引号时:在单引号内直接使用即可
  • Shell 变量传入:使用 --arg varname value,而非直接在过滤器中拼接变量

12.2 编码问题

# 处理非 UTF-8 编码 # jq 默认只支持 UTF-8 输入,遇到非 UTF-8 字符会报错 # 解决方案 1:先转换为 UTF-8 iconv -f GBK -t UTF-8 input.json | jq '.' # 解决方案 2:使用 -R 将输入视为原始字符串 jq -R '.' input.txt # 按原始字符串读取 # Unicode 转义输出 jq '.' data.json # 默认对非 ASCII 字符进行转义 jq -r '.key' data.json # -r 输出原始字符串(不转义)

12.3 性能优化

# 处理大文件的最佳实践 # 1. 使用流式处理(避免 -s) # 劣:将整个文件加载到内存 jq -s '.[] | select(.type == "error")' huge.json # 优:流式处理,逐行输出 jq -cn 'inputs | select(.type == "error")' huge.json # 2. 只提取需要的字段,避免全量加载 jq -c '{id, name, timestamp}' huge.json # 3. 使用 --stream 进行流式解析(处理超大型文件) jq --stream 'select(length == 2) | .[1]' huge.json # 4. 配合 head 限制处理量 head -1000 huge.json | jq '.'
性能建议:
  • 小文件(< 10MB):无需考虑优化,直接使用标准模式
  • 中等文件(10-100MB):使用 inputs 流式处理,避免 -s
  • 大文件(> 100MB):使用 --stream 选项,或先截取样本分析
  • 超大文件(> 1GB):考虑使用专用工具(如 jq 的 C 库绑定)或分批处理

12.4 调试技巧

# 调试技巧 1:使用 empty 跳过错误 jq 'try .field catch empty' data.json # 调试技巧 2:逐步构建过滤器 # 先从简单查询开始,逐步增加复杂度 jq '.' data.json # 步骤 1:查看整体结构 jq '.users' data.json # 步骤 2:查看特定字段 jq '.users[] | .name' # 步骤 3:深入遍历 jq '.users[] | select(.age > 18) | .name' # 步骤 4:增加过滤条件 # 调试技巧 3:调试输出 -- 查看中间结果 jq '.users | debug | .[] | {name, age}' data.json # debug 函数会在 stderr 输出中间结果 # 调试技巧 4:使用 -e 检查匹配结果 jq -e '.users[0]' data.json >/dev/null && echo "找到用户" # 调试技巧 5:在脚本中捕获 jq 错误 result=$(jq -e '.nonexistent' data.json 2>/dev/null) || echo "字段不存在"

学习 jq 的推荐路径

  1. 官方手册 -- jq Manual(最权威的参考)
  2. jq 教程 -- 官方交互式教程
  3. jq play -- jqplay.org(在线实验场)
  4. jq 备忘单 -- 各大技术社区的 jq Cheatsheet
  5. 实践出真知 -- 在每天的 API 调试、配置管理、日志分析中刻意使用 jq

十三、核心总结

jq 学习路线图

掌握 jq 是一个从基础到高阶的逐步积累过程,以下是建议的学习路径:

属性查询 数组操作 对象转换 管道组合 条件过滤 字符串处理 高级函数 实战应用

13.1 jq 命令速查表

分类命令/过滤器说明
基础jq '.' file格式化输出 JSON
jq '.key' file获取属性值
jq '.a.b.c' file获取嵌套属性
jq '.key1, .key2' file获取多个属性
数组jq '.[]' file展开数组
jq '.[0]' file按索引访问
jq '.[1:3]' file数组切片
jq 'map(.key)' file映射变换
过滤jq 'select(.k > v)' file条件过滤
jq 'unique' file数组去重
jq 'group_by(.k)' file分组
jq 'sort_by(.k)' file排序
变换jq '{new: .old}' file字段重命名
jq 'del(.key)' file删除字段
jq '. + {k: v}' file添加字段
jq 'with_entries(f)' file键值对变换
输出jq -r '.key' file原始字符串输出
jq -c '.' file紧凑格式输出
jq -s '.' f1 f2合并多个文件
jq '.[] | @csv' fileCSV 格式输出

13.2 三大黄金原则

原则一:理解流式处理

jq 的每个过滤器接收一个输入值,产生零个或多个输出值。管道将左侧的输出逐个传递给右侧。这种"数据流"模型是 jq 高效和灵活的根源。理解 .[](展开为多个值)与 map()(保持为一个数组)的区别至关重要。

原则二:从简单到复杂,逐步构建

永远不要试图一次写出复杂的 jq 过滤器。正确的做法是:先用 jq '.' 查看数据结构,然后逐步添加属性访问、数组操作、过滤条件、变换函数。每步验证结果,确保理解当前操作的含义。遇到复杂需求时,可以定义中间变量或拆分多个步骤。

原则三:管道组合而非一站式完成

jq 的精髓在于通过管道组合简单的过滤器来完成复杂的任务。不要试图在一个过滤器中完成所有事情。将任务分解为:数据提取 → 过滤筛选 → 数据变换 → 格式化输出 四个阶段。每个阶段使用最合适的过滤器和函数。

13.3 常用组合模式集锦

# 模式 1:提取 → 过滤 → 排序 → 截取 jq 'map(select(.active)) | sort_by(-.score) | .[:10] | [.[] | {name, score}]' # 解释:取活跃用户,按分数降序,取前十,只保留 name 和 score # 模式 2:分组 → 聚合 → 排序 jq 'group_by(.category) | map({category: .[0].category, count: length, total: map(.amount) | add}) | sort_by(-.total)' # 解释:按类别分组,统计每类数量和金额总和,按总额降序排列 # 模式 3:嵌套展开 → 关联 → 输出 jq '[.users[] | .orders[] as $o | {user: .name, product: $o.product, price: $o.price}]' # 解释:将用户-订单的一对多关系展开为扁平记录 # 模式 4:递归修改 → 条件转换 jq 'walk(if type == "string" then gsub("password"; "***") elif type == "number" then . * 2 else . end)' # 解释:递归遍历 JSON,将密码字段替换为 ***,数值翻倍 # 模式 5:多文件合并 → 关联分析 jq -s '{users: .[0].users, orders: .[1].orders}' users.json orders.json

13.4 推荐学习资源

资源类型说明
jq 官方手册文档jq 的权威参考手册,涵盖所有内置函数和操作符
jq playground在线工具在线实验场,实时测试 jq 过滤器
官方教程教程官方交互式入门教程
GitHub 仓库代码jq 源码和 issue 跟踪
jq 高级特性文档reduce、foreach 等高级用法详解

最终寄语

jq 是现代开发者的瑞士军刀,它让 JSON 数据处理变得像 Shell 文本处理一样流畅自然。初学时可能会对 jq 的语法感到陌生,但一旦掌握了它的核心概念(管道、过滤器、迭代),你会发现处理 JSON 数据从未如此优雅高效。

记住:每一个数据结构都有最适合它的查询语言。对于 JSON,jq 就是那个答案。每天多用、多看 jq 的 --help 输出和官方手册,jq 会成为你数据处理工具箱中最锋利的刀刃。

10001