1. 为什么每个Linux用户都应该掌握jq
在终端处理JSON数据就像试图用剪刀裁切钢板——原始工具完全不对口。我第一次面对数百行的JSON响应时,用grep和awk折腾了整整下午,直到发现jq这个"JSON瑞士军刀"。这个轻量级的命令行处理器不仅能漂亮地格式化JSON,更提供了强大的查询、转换能力,甚至支持自定义函数。
如果你经常需要:
- 分析API返回的复杂JSON
- 从Kubernetes配置中提取特定字段
- 处理AWS CLI的输出结果
- 转换日志格式或配置文件
那么jq将成为你终端工具箱中最闪亮的那个工具。它用类似jQuery的语法(但更简洁),让JSON处理变得像用管道组合Linux命令一样自然。
2. jq核心功能全景图
2.1 基础过滤与格式化
安装jq只需一行命令(Ubuntu示例):
sudo apt-get install jq最基本的用法是美化输出。对比直接curl API和通过jq处理的效果:
# 原始JSON(压缩在一行) curl -s https://api.github.com/users/octocat # 格式化后 curl -s https://api.github.com/users/octocat | jq '.'注意:jq默认会对特殊字符进行转义显示,使用
-r参数可输出原始字符串
2.2 字段提取的多种姿势
提取顶级字段:
jq '.login' <<< '{"login":"octocat","id":583231}'处理嵌套结构(GitHub API示例):
curl -s https://api.github.com/repos/stedolan/jq | jq '.owner.login'安全访问可能不存在的字段:
echo '{"a":1}' | jq '.b? // "default"'2.3 数组的魔法操作
假设我们有个包含多个仓库的JSON数组:
[ {"name":"jq","stars":25000}, {"name":"docker","stars":65000} ]常用数组操作:
# 提取所有仓库名 jq '.[].name' # 过滤stars超过30000的项目 jq 'map(select(.stars > 30000))' # 计算总stars数 jq '[.[].stars] | add'2.4 高级转换技巧
jq真正的威力在于数据转换。比如将API响应转为CSV:
curl -s https://api.github.com/orgs/docker/repos | \ jq -r '["Name","Stars"], (.[] | [.name, .stargazers_count]) | @csv'构建新JSON结构:
echo '{"a":1,"b":2}' | jq '{sum: (.a+.b), product: (.a*.b)}'3. 实战中的jq黑科技
3.1 处理AWS CLI输出
获取所有正在运行的EC2实例ID:
aws ec2 describe-instances | \ jq -r '.Reservations[].Instances[] | select(.State.Name=="running") | .InstanceId'3.2 Kubernetes资源分析
统计所有namespace的pod数量:
kubectl get pods -A -o json | \ jq '[.items[] | {ns: .metadata.namespace}] | group_by(.ns) | map({ns: .[0].ns, count: length})'3.3 日志处理黄金组合
配合grep提取特定日志字段:
grep 'ERROR' app.log | jq -R 'fromjson? | select(.level == "ERROR") | {time, message}'经验:
-R处理原始文本,fromjson?安全尝试解析JSON,问号表示解析失败时跳过而非报错
4. 性能优化与调试技巧
4.1 处理大文件的最佳实践
对于GB级JSON文件:
# 流式处理避免内存爆炸 jq --stream 'select(...)' huge.json # 配合split处理 split -l 10000 big.json chunk_ for f in chunk_*; do jq '...' $f >> result.json done4.2 调试复杂表达式
使用debug函数:
echo '{"a":{"b":1}}' | jq '.a | debug | .b'分步验证:
# 先测试第一部分 jq '.a' file.json # 确认结果后再继续 jq '.a | .b' file.json4.3 jq与shell的完美配合
变量传递技巧:
threshold=1000 jq --arg th "$threshold" '.[] | select(.value > ($th|tonumber))' data.json处理多文件:
jq -n '[inputs] | add' file1.json file2.json5. 我踩过的那些坑
字段名包含特殊字符:遇到
{"foo.bar":1}这样的key时,必须用双引号:jq '."foo.bar"' # 正确 jq .foo.bar # 会报错浮点数精度问题:jq使用IEEE754浮点数,大整数计算可能不准:
echo '{"a":12345678901234567890}' | jq '.a+1' # 可能得到不精确结果空管道陷阱:当输入为空数组时,
.[]会不产生任何输出(包括null):echo '[]' | jq '.[] | .field' # 无输出 echo '[]' | jq '.[]? | .field // empty' # 更安全的写法性能杀手:在大型数据集上避免多次扫描,比如:
# 低效写法(扫描两次) jq '(.[] | select(.a>1)) as $x | (.[] | select(.b<0)) as $y | [$x,$y]' # 高效写法(扫描一次) jq '[.[] | select(.a>1 or .b<0) | {a,b}]'
6. 扩展你的jq武器库
6.1 自定义函数
在~/.jq中定义常用函数:
# ~/.jq def total(field): map(field) | add; def avg(field): total(field)/length;使用:
jq 'include "mylib"; avg(.score)' data.json6.2 模块化开发
创建可复用的jq模块:
# stats.jq def summary: { count: length, sum: add, avg: add/length };调用:
jq 'include "stats"; [.[].value] | summary' data.json6.3 与其它工具联用
jq+vim的强大组合:
# 在vim中格式化当前JSON :%!jq .jq+curl监控API:
watch -n 5 'curl -s https://api.example.com/status | jq "{load: .system.load, uptime: .uptime}"'7. 真实世界案例分析
7.1 重构混乱的配置
原始混乱的JSON:
{"server":{"host":"example.com","ports":[80,443]},"features":{"ssl":true}}用jq转换为更清晰的YAML:
jq -r '{ host: .server.host, ports: .server.ports, use_ssl: .features.ssl } | to_entries[] | "\(.key): \(.value)"' config.json7.2 分析GitHub贡献图
获取自己的提交日历:
curl -s https://api.github.com/users/yourname/events | \ jq 'map(select(.type=="PushEvent")) | group_by(.created_at[:10]) | map({date: .[0].created_at[:10], count: length})'7.3 动态生成Ansible清单
从云API生成主机列表:
aws ec2 describe-instances | \ jq -r '.Reservations[].Instances[] | select(.Tags[]?.Key=="Env" and .Tags[]?.Value=="prod") | [.PrivateIpAddress, .Tags[]? | select(.Key=="Name").Value] | @tsv'8. 性能对比测试
处理1GB JSON文件的各种方法对比:
| 方法 | 耗时 | 内存占用 | 适用场景 |
|---|---|---|---|
| jq基本过滤 | 12.3s | 1.2GB | 简单提取字段 |
| jq流模式(--stream) | 28.7s | 15MB | 超大文件处理 |
| Python json模块 | 8.9s | 2.1GB | 复杂业务逻辑 |
| grep+简单处理 | 4.2s | 10MB | 行级简单匹配 |
实测建议:对于>100MB的文件,考虑使用
--stream模式;对于需要复杂计算的场景,Python可能更合适
9. 替代方案对比
当jq不是最佳选择时:
Python的jmespath:
import jmespath expression = jmespath.compile("locations[?state == 'WA'].name | sort(@)")Go的gjson:
echo '{"name":{"first":"Janet","last":"Prichard"}}' | \ gjson 'name.last'Node.js的jq-node:
const jq = require('node-jq') const options = { input: 'string' } jq.run('.name', {}, options).then(console.log)
选择依据:
- 需要复杂业务逻辑 → Python
- 处理超大数据集 → Go
- 已有Node.js环境 → jq-node
- 快速命令行处理 → jq
10. 我的jq备忘录
最后分享我的~/.jq常用片段:
# 提取多个字段为表格 def table($fields): [$fields] as $headers | ($headers | @tsv), (.[] | [.[$headers[]]] | @tsv); # 安全数值计算 def safe_div($a; $b): if $b == 0 then null else $a/$b end; # 深度合并对象 def deep_merge($other): . as $self | reduce ($other|keys[]) as $key ($self; if $self|has($key) and ($self[$key]|type) == "object" then .[$key] = ($self[$key] | deep_merge($other[$key])) else .[$key] = $other[$key] end);使用示例:
# 生成表格视图 jq -r 'include "mylib"; table(["name", "age"])' data.json # 安全计算百分比 jq 'include "mylib"; safe_div(.success; .total) * 100' stats.json