📚 命令概述
🔍 grep - 全局正则表达式打印工具
命令全称 : Global Regular Expression Print
核心作用 : 在文件中搜索匹配指定模式的文本行
主要用途:
- 查找包含特定关键字的日志行
- 过滤日志内容
- 统计匹配行数
- 实时监控日志中的错误信息
一句话总结: grep 是"查找器",用于快速定位你想要的内容
📊 awk - 文本分析工具
命令全称 : Aho, Weinberger, Kernighan(三位创始人名字首字母)
核心作用 : 按行处理文本,按列提取和分析数据
主要用途:
- 提取日志中的特定字段(如时间、IP、状态码)
- 统计和计算(如求和、平均值、计数)
- 条件过滤和格式化输出
- 复杂的文本数据分析
一句话总结: awk 是"分析器",擅长处理结构化数据和统计计算
✏️ sed - 流编辑器
命令全称 : Stream Editor
核心作用 : 对文本进行批量编辑和转换
主要用途:
- 替换文本内容
- 删除特定行
- 插入和追加内容
- 提取指定行
- 批量修改文件
一句话总结: sed 是"编辑器",用于批量修改和处理文本
📖 详细用法介绍
一、grep - 文本搜索命令
🎯 grep 是什么?
grep 是 Linux 中最常用的文本搜索工具,它能在文件中查找包含指定模式的行,并将这些行输出。就像在书中用荧光笔标记关键字一样,grep 帮你快速找到日志中的重要信息。
💡 为什么需要 grep?
在生产环境中,日志文件通常有几十万甚至上百万行,手动查找几乎不可能。grep 可以在几秒内从海量日志中找到你需要的错误信息、异常堆栈或特定事件。
📋 grep 常见使用场景
场景一:查看完整异常堆栈
问题: Java 异常堆栈通常是多行的,只看一行看不到完整信息
解决方案:
perl
# 显示匹配行及其后 50 行
grep -A 50 "java.lang.NullPointerException" a.log
# 结合 less 分页查看
grep -A 50 "java.lang.NullPointerException" a.log | less
要点说明:
-
-A N参数:显示匹配行之后的 N 行(After) -
异常堆栈通常需要看后续 20-50 行才能找到根本原因
-
less命令用于分页查看:↑↓键滚动G跳到末尾q退出
场景二:实时监控新日志
问题: 需要实时观察线上服务是否出现错误
解决方案:
bash
# 实时监控并显示异常及后续 50 行
tail -f a.log | grep -A 50 "java.lang.NullPointerException"
# 忽略大小写实时监控
tail -f a.log | grep -i -A 50 "error"
要点说明:
tail -f:实时追踪文件新增内容- 结合
grep过滤关键异常 Ctrl + C停止监控-i参数忽略大小写
场景三:查找历史/压缩日志
问题: 需要在多个日志文件或压缩日志中查找问题
查找所有 .log 文件:
bash
# -H 参数显示文件名
grep -H -A 50 "java.lang.NullPointerException" *.log
查找压缩日志 (.gz):
arduino
# zgrep 直接处理 .gz 文件,无需解压
zgrep -H -A 50 "java.lang.NullPointerException" *.gz
要点说明:
-H参数:显示匹配内容所在的文件名zgrep:专门处理压缩文件,无需手动解压*.log和*.gz:通配符匹配所有相关文件
场景四:统计异常出现次数
问题: 判断异常是偶发还是频繁发生
解决方案:
perl
# 统计单个文件
grep -c "java.lang.NullPointerException" a.log
# 统计所有日志文件
grep -c "java.lang.NullPointerException" *.log
输出示例:
c
app.log:15
app.log.1:3
app.log.2:0
要点说明:
-c参数:只显示匹配行的数量,不显示内容- 快速判断问题严重程度
📊 grep 参数速查表
| 参数 | 含义 | 作用 | 示例 |
|--------|----------------|------------------|--------------------------|-------------|
| -A N | After | 显示匹配行之后的 N 行 | grep -A 10 "error" log |
| -B N | Before | 显示匹配行之前的 N 行 | grep -B 5 "error" log |
| -C N | Context | 显示匹配行上下各 N 行 | grep -C 25 "error" log |
| -i | ignore case | 忽略大小写 | grep -i "ERROR" log |
| -v | invert | 反向匹配(不包含) | grep -v "DEBUG" log |
| -H | with filename | 显示匹配的文件名 | grep -H "error" *.log |
| -h | no filename | 不显示文件名 | grep -h "error" *.log |
| -c | count | 统计匹配行数 | grep -c "error" log |
| -n | line number | 显示行号 | grep -n "error" log |
| -r | recursive | 递归搜索目录 | grep -r "error" /logs/ |
| -E | extended regex | 使用扩展正则 | `grep -E "error | warn" log` |
| -w | word | 完整单词匹配 | grep -w "error" log |
💡 grep 实战技巧
- 看异常必须用
-A - 单行看不到完整堆栈信息 - 实时监控用
tail -f | grep - 第一时间发现问题 - 压缩日志用
zgrep - 省时省力,无需解压 - 批量查找加
-H - 知道是哪个文件出的问题 - 统计频率用
-c - 判断问题严重程度 - 组合使用
-A -B -C - 查看完整上下文
二、awk - 文本分析命令
🎯 awk 是什么?
awk 是一个强大的文本分析工具,它把每一行看作一条记录,把每一列看作一个字段。就像 Excel 处理表格数据一样,awk 可以提取、计算、统计日志中的结构化数据。
💡 为什么需要 awk?
日志通常是结构化的(如:时间 | IP | 状态码 | 响应时间),当你需要:
- 只看某几列数据
- 统计某个字段的总和或平均值
- 根据条件过滤数据
- 进行复杂的数据分析
这时 awk 就是最佳选择!
📋 awk 基本概念
awk 如何看待文本:
bash
2025-12-19 10:30:15 192.168.1.100 GET /api/user 200 150ms
$1 $2 $3 $4 $5 $6 $7
字段1 字段2 字段3 字段4 字段5 字段6 字段7
awk 的工作流程:
- 读取一行
- 按分隔符(默认空格/tab)分割成字段
- 对字段进行处理
- 输出结果
- 继续下一行
📋 awk 常见使用场景
场景一:提取特定列
问题: 只想看日志中的时间和错误信息
解决方案:
dart
# 提取第1列(时间)和第5列(错误信息)
awk '{print $1, $5}' app.log
# 提取包含ERROR的行的特定字段
awk '/ERROR/ {print $1, $2, $NF}' app.log
# 提取第一列和最后一列
awk '{print $1, $NF}' app.log
输出示例:
yaml
2025-12-19 NullPointerException
2025-12-19 TimeoutException
要点说明:
$1, $2, $3...:表示第1、2、3列$NF:表示最后一列(Number of Fields)$0:表示整行内容
场景二:统计分析
问题: 统计各状态码出现次数、计算平均响应时间
统计状态码出现次数:
css
# 假设状态码在第9列
awk '{count[$9]++} END {for(code in count) print code, count[code]}' access.log
输出示例:
200 15234
404 89
500 23
计算响应时间统计:
makefile
# 假设响应时间在第10列
awk '{sum+=$10; count++} END {print "总计:", sum, "平均:", sum/count}' access.log
输出示例:
makefile
总计: 125000 平均: 250
统计 IP 访问次数 TOP 10:
bash
# 第1列是IP
awk '{ip[$1]++} END {for(i in ip) print ip[i], i}' access.log | sort -rn | head -10
输出示例:
yaml
1523 192.168.1.100
892 192.168.1.101
456 192.168.1.102
要点说明:
count[$9]++:使用关联数组统计END {}:所有行处理完后执行sum+=:累加计算
场景三:条件过滤
问题: 只看响应时间超过1秒的请求
解决方案:
dart
# 显示响应时间大于1000ms的请求
awk '$10 > 1000 {print $0}' access.log
# 显示特定时间段的日志
awk '$1 >= "2025-12-19 10:00:00" && $1 <= "2025-12-19 11:00:00"' app.log
# 显示状态码为5xx的错误
awk '$9 ~ /^5/ {print $0}' access.log
# 显示状态码不是200的请求
awk '$9 != 200 {print $0}' access.log
要点说明:
$10 > 1000:数值比较$1 >= "xxx" && $1 <= "yyy":字符串比较和逻辑运算$9 ~ /^5/:正则匹配(以5开头)!=:不等于
场景四:格式化输出
问题: 让输出更易读,添加表头和格式
解决方案:
swift
# 格式化输出,添加表头
awk 'BEGIN {print "时间\t\t\t状态码\t响应时间"}
{printf "%s\t%s\t%s\n", $1, $9, $10}' access.log
# 计算并格式化输出百分比
awk '{total++; if($9=="200") success++}
END {printf "成功率: %.2f%%\n", (success/total)*100}' access.log
# 添加行号
awk '{print NR, $0}' app.log
输出示例:
yaml
时间 状态码 响应时间
2025-12-19 200 150
2025-12-19 404 50
成功率: 98.50%
要点说明:
BEGIN {}:处理第一行之前执行printf:格式化输出(类似C语言)%.2f:保留两位小数NR:当前行号
📊 awk 内置变量速查表
| 变量 | 含义 | 说明 | 示例 |
|---|---|---|---|
$0 |
整行内容 | 当前处理的整行 | awk '{print $0}' |
$1, $2, $n |
第n个字段 | 按分隔符分割的字段 | awk '{print $1, $3}' |
$NF |
最后一个字段 | Number of Fields | awk '{print $NF}' |
NR |
行号 | Number of Records | awk 'NR==10 {print $0}' |
NF |
字段数 | 当前行有多少列 | awk '{print NF}' |
FS |
字段分隔符 | Field Separator(默认空格) | awk 'BEGIN{FS=","}' |
OFS |
输出字段分隔符 | Output Field Separator | awk 'BEGIN{OFS="\t"}' |
📊 awk 常用参数和操作符
| 参数/操作符 | 作用 | 示例 |
|-------------------|---------|---------------------------|-----|----------------|---|------------|
| -F | 指定字段分隔符 | awk -F',' '{print $1}' |
| ~ | 正则匹配 | awk '$0 ~ /ERROR/' |
| !~ | 正则不匹配 | awk '$0 !~ /DEBUG/' |
| == | 等于 | awk '$1 == "200"' |
| != | 不等于 | awk '$1 != "200"' |
| > < >= <= | 大小比较 | awk '$10 > 1000' |
| && | 逻辑与 | awk '$1==200 && $2>100' |
| ` | | ` | 逻辑或 | `awk '1==200 | | 1==201'` |
💡 awk 实战技巧
-
指定分隔符 : 日志不是空格分隔时用
-Fbashawk -F',' '{print $1}' # CSV文件 awk -F':' '{print $1}' # 冒号分隔 -
多条件过滤 : 使用
&&和||dartawk '$9==200 && $10>1000 {print $0}' -
统计去重: 使用关联数组
scssawk '{arr[$1]++} END {print length(arr)}' # 统计唯一IP数 -
BEGIN 和 END:
BEGIN:处理数据前执行(设置变量、打印表头)END:处理数据后执行(打印统计结果)
-
格式化输出 : 使用
printf而不是printswiftawk '{printf "%-20s %10d\n", $1, $2}'
三、sed - 流编辑命令
🎯 sed 是什么?
sed 是一个流编辑器(Stream Editor),它可以对文本进行批量编辑操作。就像 Word 的"查找替换"功能,但 sed 更强大,可以处理整个文件或多个文件,支持正则表达式,还能删除、插入、提取行。
💡 为什么需要 sed?
当你需要:
- 批量替换日志中的敏感信息
- 删除调试日志(DEBUG 行)
- 提取特定行号或匹配的行
- 在特定位置插入内容
- 不打开文件就能修改内容
这时 sed 就是最佳工具!
📋 sed 基本概念
sed 的工作流程:
- 读取一行到模式空间(Pattern Space)
- 执行编辑命令
- 输出结果到标准输出
- 继续下一行
sed 命令格式:
arduino
sed [选项] '命令' 文件
sed [选项] -e '命令1' -e '命令2' 文件
📋 sed 常见使用场景
场景一:内容替换(最常用)
问题: 需要批量替换日志中的文本
基本替换:
c
# 替换每行第一个ERROR为[错误]
sed 's/ERROR/[错误]/' app.log
# 全局替换(替换所有,不只是第一个)
sed 's/ERROR/[错误]/g' app.log
# 替换并保存到新文件
sed 's/ERROR/[错误]/g' app.log > app_new.log
# 直接修改原文件(谨慎使用!)
sed -i 's/ERROR/[错误]/g' app.log
# 修改前备份原文件
sed -i.bak 's/ERROR/[错误]/g' app.log
高级替换:
bash
# 只替换包含特定模式的行
sed '/2025-12-19/s/ERROR/[错误]/g' app.log
# 替换特定行号(第10行)
sed '10s/ERROR/[错误]/g' app.log
# 替换行号范围(10-20行)
sed '10,20s/ERROR/[错误]/g' app.log
# 忽略大小写替换
sed 's/error/[错误]/gi' app.log
要点说明:
s/old/new/:替换命令格式g:全局替换(global)i:忽略大小写(ignore case)-i:直接修改文件(in-place)
场景二:删除行
问题: 删除日志中不需要的内容
解决方案:
bash
# 删除空行
sed '/^$/d' app.log
# 删除包含DEBUG的行
sed '/DEBUG/d' app.log
# 删除第1-10行
sed '1,10d' app.log
# 删除最后一行
sed '$d' app.log
# 删除第5行
sed '5d' app.log
# 删除第3行到最后一行
sed '3,$d' app.log
# 删除不包含ERROR的行(保留ERROR行)
sed '/ERROR/!d' app.log
组合删除:
bash
# 删除空行和DEBUG行
sed -e '/^$/d' -e '/DEBUG/d' app.log
# 或使用分号
sed '/^$/d; /DEBUG/d' app.log
要点说明:
d:删除命令(delete)^$:正则表达式,表示空行!d:反向操作,删除不匹配的行
场景三:提取特定行
问题: 只想看日志中的某些行
解决方案:
bash
# 只显示包含ERROR的行(类似grep)
sed -n '/ERROR/p' app.log
# 显示第10-20行
sed -n '10,20p' app.log
# 显示第一行
sed -n '1p' app.log
# 显示最后一行
sed -n '$p' app.log
# 显示第1行和最后一行
sed -n '1p;$p' app.log
# 显示匹配行及其后5行
sed -n '/ERROR/,+5p' app.log
# 显示从匹配行到文件末尾
sed -n '/ERROR/,$p' app.log
# 显示两个模式之间的内容
sed -n '/开始标记/,/结束标记/p' app.log
要点说明:
-n:静默模式,不自动打印p:打印命令(print),+5:当前行及后5行模式1,模式2:从模式1到模式2之间的行
场景四:插入和追加
问题: 在特定位置添加内容
解决方案:
bash
# 在匹配行前插入内容
sed '/ERROR/i ====== 发现错误 ======' app.log
# 在匹配行后追加内容
sed '/ERROR/a ====== 错误结束 ======' app.log
# 在文件开头插入
sed '1i 日志开始时间: 2025-12-19' app.log
# 在文件末尾追加
sed '$a 日志结束' app.log
# 在第10行前插入
sed '10i 这是插入的内容' app.log
# 插入多行(使用\n)
sed '/ERROR/i ======\n错误详情\n======' app.log
要点说明:
i:在匹配行前插入(insert)a:在匹配行后追加(append)$:最后一行
场景五:多个操作组合
问题: 需要同时进行多种编辑操作
解决方案:
ini
# 方法1:使用多个 -e
sed -e '/^$/d' -e 's/ERROR/[错误]/g' -e '/DEBUG/d' app.log
# 方法2:使用分号分隔
sed '/^$/d; s/ERROR/[错误]/g; /DEBUG/d' app.log
# 方法3:使用多行(可读性更好)
sed '
/^$/d
s/ERROR/[错误]/g
/DEBUG/d
' app.log
# 复杂处理示例
sed -n '
/ERROR/!d # 只保留ERROR行
s/ERROR/[错误]/g # 替换ERROR
s/WARN/[警告]/g # 替换WARN
p # 打印结果
' app.log
📊 sed 命令速查表
| 命令 | 作用 | 示例 |
|---|---|---|
s/old/new/ |
替换(substitute) | sed 's/ERROR/错误/' |
d |
删除(delete) | sed '/DEBUG/d' |
p |
打印(print) | sed -n '/ERROR/p' |
i |
插入(insert) | sed '/ERROR/i 标记' |
a |
追加(append) | sed '/ERROR/a 标记' |
c |
替换整行(change) | sed '/ERROR/c 错误行' |
y |
字符转换 | sed 'y/abc/ABC/' |
q |
退出(quit) | sed '10q' |
📊 sed 参数速查表
| 参数 | 作用 | 示例 |
|---|---|---|
-n |
静默模式,不自动打印 | sed -n '/ERROR/p' |
-e |
执行多个编辑命令 | sed -e 's/a/b/' -e 's/c/d/' |
-i |
直接修改文件 | sed -i 's/old/new/g' file |
-i.bak |
修改前备份 | sed -i.bak 's/old/new/g' file |
-r |
使用扩展正则 | sed -r 's/[0-9]+/NUM/g' |
-f |
从文件读取命令 | sed -f script.sed file |
📊 sed 地址范围
| 地址 | 含义 | 示例 |
|---|---|---|
n |
第n行 | sed '5d' 删除第5行 |
n,m |
第n到m行 | sed '10,20d' 删除10-20行 |
n,+m |
第n行及后m行 | sed '10,+5d' 删除10行及后5行 |
n~m |
从n开始每m行 | sed '1~2d' 删除奇数行 |
$ |
最后一行 | sed '$d' 删除最后一行 |
/pattern/ |
匹配的行 | sed '/ERROR/d' 删除ERROR行 |
/p1/,/p2/ |
从p1到p2 | sed '/开始/,/结束/d' |
💡 sed 实战技巧
-
先测试再修改 : 去掉
-i先看效果bashsed 's/ERROR/错误/g' app.log # 先预览 sed -i 's/ERROR/错误/g' app.log # 确认后再修改 -
备份原文件 : 使用
-i.bakcsed -i.bak 's/ERROR/错误/g' app.log # 生成app.log.bak备份 -
使用正则表达式: 更灵活的匹配
csssed 's/[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}/IP_HIDDEN/g' -
引用匹配内容 : 使用
&bashsed 's/ERROR/【&】/g' # ERROR变成【ERROR】 -
分组引用 : 使用
\1\2inised 's/$[0-9]*$-$[0-9]*$/\2-\1/g' # 交换两个数字
四、grep + awk + sed 组合使用
🎯 三剑客协作
在实际工作中,这三个命令经常组合使用,各司其职:
- grep:过滤出需要的行
- awk:提取和统计数据
- sed:格式化和替换内容
📋 实战组合案例
案例1:分析错误日志
bash
# 1. 提取ERROR日志,统计各类错误次数
grep "ERROR" app.log | awk '{print $5}' | sort | uniq -c | sort -rn
# 输出示例:
# 152 NullPointerException
# 89 TimeoutException
# 23 IOException
流程说明:
grep "ERROR":过滤出ERROR行awk '{print $5}':提取第5列(异常类型)sort:排序uniq -c:统计重复次数sort -rn:按数量倒序
案例2:分析慢请求
perl
# 查找响应时间>1秒的请求,格式化输出
grep "access" app.log | awk '$10 > 1000 {print $1, $7, $10}' | sed 's/ms/毫秒/g'
# 输出示例:
# 2025-12-19 /api/user 1250毫秒
# 2025-12-19 /api/order 1800毫秒
流程说明:
grep "access":过滤访问日志awk '$10 > 1000':过滤响应时间>1000msprint $1, $7, $10:提取时间、URL、响应时间sed 's/ms/毫秒/g':替换单位
案例3:统计每小时错误数
bash
# 统计每小时的ERROR数量
grep "ERROR" app.log | awk '{print substr($1,1,13)}' | sort | uniq -c
# 输出示例:
# 45 2025-12-19 10
# 89 2025-12-19 11
# 23 2025-12-19 12
流程说明:
grep "ERROR":过滤ERROR行awk '{print substr($1,1,13)}':提取时间到小时(前13个字符)sort | uniq -c:统计每小时出现次数
案例4:清理和格式化异常堆栈
perl
# 提取异常堆栈并清理格式
grep -A 20 "Exception" app.log | sed '/^$/d' | awk '{print NR, $0}'
# 输出示例:
# 1 java.lang.NullPointerException
# 2 at com.example.Service.method(Service.java:100)
# 3 at com.example.Controller.handle(Controller.java:50)
流程说明:
grep -A 20 "Exception":提取异常及后20行sed '/^$/d':删除空行awk '{print NR, $0}':添加行号
案例5:分析API调用情况
bash
# 统计API调用次数TOP10
grep "API" access.log | awk '{print $7}' | sed 's/?.*//g' | sort | uniq -c | sort -rn | head -10
# 输出示例:
# 1523 /api/user
# 892 /api/order
# 456 /api/product
流程说明:
grep "API":过滤API请求awk '{print $7}':提取URLsed 's/?.*//g':去掉查询参数sort | uniq -c:统计次数sort -rn | head -10:取TOP10
案例6:生成错误报告
bash
# 生成今日错误统计报告
{
echo "===== 错误日志报告 ====="
echo "日期: $(date +%Y-%m-%d)"
echo ""
echo "错误总数:"
grep -c "ERROR" app.log
echo ""
echo "错误类型分布:"
grep "ERROR" app.log | awk '{print $5}' | sort | uniq -c | sort -rn
echo ""
echo "错误时间分布:"
grep "ERROR" app.log | awk '{print substr($1,1,13)}' | sort | uniq -c
} | tee error_report.txt
五、实战技巧总结
🎯 选择合适的工具
| 需求 | 推荐工具 | 原因 |
|---|---|---|
| 查找关键字 | grep | 最快最简单 |
| 提取特定列 | awk | 专门处理列数据 |
| 统计计算 | awk | 支持数学运算 |
| 替换文本 | sed | 批量替换效率高 |
| 删除行 | sed | 灵活的行操作 |
| 复杂分析 | awk | 功能最强大 |
| 组合操作 | grep+awk+sed | 各取所长 |
💡 性能优化建议
-
先过滤再处理: 用 grep 先缩小范围
luagrep "ERROR" huge.log | awk '{统计}' # 好 awk '/ERROR/ {统计}' huge.log # 较慢 -
避免不必要的管道: 能一步完成不要分多步
perlawk '/ERROR/ {print $1}' log # 好 grep "ERROR" log | awk '{print $1}' # 多余 -
使用合适的工具: 不要用 sed 做 awk 的事
bashawk '{print $1}' log # 好 sed 's/.* $[^ ]*$$/\1/' log # 复杂且慢
🔍 调试技巧
-
逐步调试: 一步步添加管道
perlgrep "ERROR" log # 第1步:看看过滤结果 grep "ERROR" log | awk '{print $5}' # 第2步:看看提取结果 grep "ERROR" log | awk '{print $5}' | sort | uniq -c # 第3步:完整流程 -
使用 head 限制输出: 避免刷屏
bashgrep "ERROR" huge.log | head -20 # 只看前20行 -
保存中间结果: 方便调试
luagrep "ERROR" log > error.tmp awk '{print $5}' error.tmp > types.tmp