Linux第05篇:文本处理三剑客——grep/sed/awk 从入门到实战

Linux第05篇:文本处理三剑客——grep/sed/awk 从入门到实战

本文导读:本文是系列第 5 篇,讲解 Linux 文本处理领域最重要的三个工具:grep、sed、awk。Java 开发者排查生产问题时,80% 的时间在和日志文件打交道,而这三个工具正是日志分析的核心武器。本文会讲透它们的核心用法,并结合 AI 推理日志分析给出实战案例。

关键词:Linux grep sed awk教程、Linux文本处理、grep正则表达式、awk字段处理、sed替换命令、Linux日志分析


目录

    • 一、为什么 Java 开发者必须掌握这三个工具
    • 二、grep:文本搜索的第一选择
      • 2.1 grep 基础用法
      • 2.2 上下文查看:排查问题的利器
      • 2.3 正则表达式:grep 的真正威力
      • 2.4 grep 处理二进制文件和大文件的注意事项
    • 三、sed:流编辑器,文本替换的标准工具
      • 3.1 sed 的核心用法:查找替换
      • 3.2 sed 的删除与插入操作
      • 3.3 sed 多命令组合与高级用法
    • 四、awk:结构化文本处理之王
      • 4.1 awk 的核心概念:字段与分隔符
      • 4.2 awk 的条件过滤:比 grep 更精确
      • 4.3 awk 的统计能力:日志分析的核心武器
      • 4.4 awk 处理多行记录与字段计算
    • 五、三剑客组合实战:管道连接威力倍增
    • 六、AI 大模型日志分析实战案例
    • 七、常见问题解答(FAQ)
      • ❓ Q1:grep、sed、awk 应该怎么选,有没有简单的判断标准?
      • ❓ Q2:sed 和 awk 都能做替换,该用哪个?
      • ❓ Q3:处理几个GB的大日志文件,这些工具会不会内存溢出?
      • ❓ Q4:awk 的内置变量太多记不住,有没有最常用的几个?
      • ❓ Q5:正则表达式在 grep/sed/awk 里语法一致吗?
    • 本篇小结与系列导航
      • 📚 参考资料

一、为什么 Java 开发者必须掌握这三个工具

排查生产问题时,你的应用日志文件可能有几十万行,传统的"打开文件用眼睛找"完全不现实。grep、sed、awk 这三个工具,分别解决"查找"、“替换”、"结构化处理"三类问题,组合起来几乎能应对所有文本分析场景。

它们的设计理念高度一致:都是面向"行"的流式处理工具,逐行读取输入,对每一行执行匹配/替换/计算,然后输出结果。这种设计使得它们可以通过管道(|)无缝衔接,组合成强大的处理链。理解这一点,比死记命令参数更重要。


二、grep:文本搜索的第一选择

2.1 grep 基础用法

# 基本语法:grep "搜索内容" 文件名grep"ERROR"app.log# 不区分大小写搜索(-i:ignore case)grep-i"error"app.log# 递归搜索整个目录下所有文件(-r:recursive)grep-r"OutOfMemoryError"/var/log/myapp/# 显示匹配行的行号(-n:number,排查问题时定位具体哪一行很有用)grep-n"ERROR"app.log# 反向匹配:只显示不包含某内容的行(-v:invert)grep-v"DEBUG"app.log# 统计匹配的行数而不显示内容(-c:count)grep-c"ERROR"app.log# 只显示匹配到的部分,不显示整行(-o:only matching)grep-o"user_id=[0-9]*"access.log

2.2 上下文查看:排查问题的利器

排查异常时,仅看到报错那一行往往信息不足,需要看报错前后的上下文:

# 显示匹配行及其后 5 行(-A:after)grep-A5"OutOfMemoryError"app.log# 显示匹配行及其前 5 行(-B:before)grep-B5"Connection refused"app.log# 同时显示前后各 3 行(-C:context)grep-C3"NullPointerException"app.log# 实战场景:查看异常堆栈的完整信息# Java 的异常堆栈通常跨越多行,只看匹配行本身看不到完整堆栈grep-A20"Exception in thread"app.log

💡 生产提示:排查 Java 异常时,grep -A 20 "Exception"这类命令是查看完整堆栈信息的标准做法。因为 Java 异常堆栈(stack trace)往往有几十行,光看grep "Exception"匹配到的那一行根本看不出问题出在哪个调用链上。习惯了用-A参数后,排查效率会明显提升。

2.3 正则表达式:grep 的真正威力

grep默认支持基础正则表达式(BRE),加-E参数后支持扩展正则表达式(ERE,更接近大多数编程语言的正则语法)。

# 用 -E 启用扩展正则(推荐,语法更直观,等同于 egrep 命令)grep-E"ERROR|WARN"app.log# 匹配 ERROR 或 WARN(不加-E需要用 \| 转义)# 匹配 IP 地址(简化版本,实战中够用)grep-E"[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}"access.log# 匹配特定时间范围的日志(比如 2026-06-30 的 10 点到 12 点)grep-E"2026-06-30 1[0-2]:"app.log# 匹配以特定字符串开头/结尾的行(^ 表示行首,$ 表示行尾)grep"^2026-06-30"app.log# 以这个日期开头的行grep"timeout$"app.log# 以 timeout 结尾的行# 匹配多个文件,并显示是哪个文件匹配的grep-rn"OutOfMemoryError"/var/log/myapp/*.log

⚠️ 踩坑记录grep默认的基础正则表达式(BRE)里,+?|()等元字符需要用反斜杠转义才能生效(比如grep "ERROR\|WARN"),这和大多数编程语言里正则表达式的习惯不一样,容易写错导致匹配不到任何内容却不报错。建议养成习惯,永远用-E参数(或直接用egrep命令),这样正则语法和你熟悉的 Java 正则、Python 正则基本一致,不容易踩坑。

2.4 grep 处理二进制文件和大文件的注意事项

# 在不确定文件类型的目录里递归 grep 时,二进制文件可能导致输出乱码或卡顿# 用 -I 跳过二进制文件(大写 I,等同于 --binary-files=without-match)grep-rI"ERROR"/var/log/myapp/# 对于超大文件(几个GB的日志),grep 配合管道流式处理,不会一次性加载整个文件到内存# 这是 grep 的天然优势,不用担心大文件导致内存溢出grep"ERROR"huge_access.log|wc-l# 结合 zgrep 直接搜索压缩日志,不需要先手动解压zgrep"ERROR"app.log.gz

三、sed:流编辑器,文本替换的标准工具

sed(Stream Editor)是非交互式的文本编辑工具,最常见的用法是批量替换文本内容。

3.1 sed 的核心用法:查找替换

# 基本语法:sed 's/查找内容/替换内容/' 文件名# s 表示 substitute(替换),三个斜杠分隔三部分# 默认只在终端显示替换结果,不修改原文件(用于先预览效果)sed's/ERROR/WARNING/'app.log# 默认只替换每行第一个匹配项,加 g(global)才会替换该行所有匹配项sed's/ERROR/WARNING/g'app.log# -i 直接修改原文件(in-place,没有这个参数不会真正修改文件!)sed-i's/127.0.0.1/192.168.1.100/g'application.yml# -i.bak:修改前自动创建备份(强烈推荐,比直接 -i 安全得多)sed-i.bak's/old-domain.com/new-domain.com/g'nginx.conf# 只替换指定行号范围内的内容(比如只替换第 10-20 行)sed'10,20s/DEBUG/INFO/g'app.log# 只替换匹配特定模式的行(比如只替换包含 "production" 的行里的内容)sed'/production/s/DEBUG/INFO/'config.yml

⚠️ 踩坑记录sed -i直接修改原文件且没有确认提示,这是一个高风险操作。强烈建议永远用sed -i.bak而不是裸的sed -i,多打几个字符的成本,换来的是误操作后能立刻恢复的安全网。另外要注意,macOS 自带的 sed(BSD 版本)和 Linux 的 GNU sed 在-i参数的语法上不兼容——GNU sed 的-i.bak和 BSD sed 的-i .bak(有空格)写法不同,如果你的部署脚本要跨平台运行,这里很容易因为平台差异导致脚本报错或者意外不生效。

3.2 sed 的删除与插入操作

# 删除指定行(d:delete)sed'5d'app.log# 删除第5行sed'5,10d'app.log# 删除第5到10行sed'/DEBUG/d'app.log# 删除所有包含 DEBUG 的行(清理日志常用)# 删除空行sed'/^$/d'config.yml# 在指定行前/后插入内容sed'3i\新插入的行'app.log# 在第3行之前插入(i:insert)sed'3a\新插入的行'app.log# 在第3行之后插入(a:append)# 实战:批量清理日志中的 DEBUG 级别日志行,只保留 INFO 及以上级别sed-i.bak'/\[DEBUG\]/d'application.log

3.3 sed 多命令组合与高级用法

# 用 -e 执行多个 sed 命令(一次性完成多项替换)sed-e's/ERROR/WARNING/g'-e's/INFO/NOTICE/g'app.log# 用分号分隔多个命令(效果等同于多个 -e)sed's/ERROR/WARNING/g; s/INFO/NOTICE/g'app.log# 使用正则表达式进行复杂替换(结合分组引用 \1 \2)# 把 "key=value" 格式转换成 "value=key"(交换位置)echo"name=zhangsan"|sed-E's/([a-z]+)=([a-z]+)/\2=\1/'# 输出:zhangsan=name# 实战场景:批量修改 Spring Boot 配置文件中的端口号sed-i.bak's/server.port: 8080/server.port: 8081/'application.yml# 实战场景:脱敏处理日志中的手机号(保留前3位后4位,中间用*代替)sed-E's/([0-9]{3})[0-9]{4}([0-9]{4})/\1****\2/g'access.log

💡 生产提示:处理涉及用户隐私信息的日志(手机号、身份证号、邮箱)时,sed 配合正则表达式做脱敏处理是常见的合规需求。上面手机号脱敏的例子,可以直接用在日志归档或者把日志提供给第三方分析前的预处理环节,避免敏感信息泄露。


四、awk:结构化文本处理之王

awkgrepsed更强大,它把每一行看作由多个"字段"组成的记录,特别适合处理表格化、结构化的文本(比如 Nginx access log、CSV 文件)。

4.1 awk 的核心概念:字段与分隔符

# awk 默认用空格(或连续空白)作为字段分隔符,$1 表示第1个字段,$2 表示第2个字段# $0 表示整行内容,NF 表示当前行的字段总数# 示例数据:一行典型的 Nginx access log# 192.168.1.1 - - [30/Jun/2026:10:00:00 +0800] "GET /api/users HTTP/1.1" 200 1234# 打印第1个字段(IP地址)awk'{print $1}'access.log# 打印第1个和第7个字段(IP地址和请求路径)awk'{print $1, $7}'access.log# 打印最后一个字段(用 $NF,NF是字段总数的内置变量)awk'{print $NF}'access.log# 自定义分隔符(-F 参数),处理 CSV 或自定义格式日志echo"name,age,city"|awk-F',''{print $2}'# 输出:age

4.2 awk 的条件过滤:比 grep 更精确

# 只处理满足条件的行(类似 grep,但能结合字段位置精确过滤)# 找出所有返回状态码为 500 的请求(假设状态码是第9个字段)awk'$9 == 500 {print $0}'access.log# 找出响应时间超过 1000ms 的慢请求(假设响应时间是最后一个字段)awk'$NF > 1000 {print $1, $7, $NF}'access.log# 字符串匹配过滤(~ 表示匹配正则)awk'$7 ~ /^\/api\/user/ {print $0}'access.log# 组合多个条件(&& 表示且,|| 表示或)awk'$9 == 500 && $7 ~ /^\/api/ {print $1, $7}'access.log

4.3 awk 的统计能力:日志分析的核心武器

# 统计访问次数最多的 IP(这是排查异常流量/攻击的经典命令)awk'{print $1}'access.log|sort|uniq-c|sort-rn|head-10# 用 awk 自带的累加统计:统计每个状态码出现的次数awk'{count[$9]++} END {for (code in count) print code, count[code]}'access.log# 统计总流量(假设响应大小是第10个字段,单位字节)awk'{total += $10} END {print "Total bytes:", total}'access.log# 计算平均响应时间awk'{sum += $NF; n++} END {print "Average response time:", sum/n, "ms"}'access.log# 输出格式化报表(结合 printf 让输出更整齐)awk'{count[$9]++} END {for (code in count) printf "状态码 %s: %d 次\n", code, count[code]}'access.log

💡 生产提示awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -10这条命令是排查流量异常和潜在攻击的标准武器——它统计出访问次数最高的 10 个 IP。如果发现某个 IP 在短时间内的请求量异常高(远超正常用户行为),很可能是爬虫、压测工具或者恶意攻击,可以结合这个结果进一步用防火墙规则封禁。这条命令组合值得直接记下来。

4.4 awk 处理多行记录与字段计算

# 计算每行字段数(NF),快速发现格式不规整的日志行awk'{print NF, $0}'app.log|sort-n|head-5# BEGIN 和 END 块:在处理数据前后执行的特殊逻辑awk'BEGIN {print "===== 开始分析日志 ====="} {count++} END {print "总行数:", count}'app.log# 实战:分析 AI 推理服务日志,提取每次请求的 token 消耗# 假设日志格式:[2026-06-30 10:00:00] request_id=abc123 prompt_tokens=150 completion_tokens=80awk-F'[= ]''{for(i=1;i<=NF;i++) if($i=="prompt_tokens") prompt+=$(i+1); if($i=="completion_tokens") completion+=$(i+1)} END {print "总prompt tokens:", prompt, "总completion tokens:", completion}'inference.log

五、三剑客组合实战:管道连接威力倍增

grep、sed、awk 单独使用已经很强大,组合起来威力更大。以下是几个真实生产场景的组合命令:

# 场景1:从大日志文件中筛选出某个时间段的 ERROR 日志,并统计每种错误类型的数量grep"2026-06-30"app.log|grep"ERROR"|awk-F'ERROR: ''{print $2}'|awk-F' at ''{print $1}'|sort|uniq-c|sort-rn# 场景2:找出某个用户ID相关的所有日志,脱敏处理后导出grep"user_id=10086"app.log|sed-E's/(phone=)[0-9]{7}([0-9]{4})/\1*******\2/g'>user_10086_filtered.log# 场景3:分析 Nginx 日志,找出哪些接口的 5xx 错误最多(典型的故障定位流程)awk'$9 >= 500 {print $7}'access.log|sort|uniq-c|sort-rn|head-20# 场景4:实时监控日志中的异常(结合 tail -f 实现实时报警雏形)tail-f/var/log/myapp/app.log|grep--line-buffered"ERROR"|whilereadline;doecho"检测到错误:$line"# 这里可以加发送告警通知的逻辑(企业微信/钉钉webhook)done

⚠️ 踩坑记录:上面场景4里的grep --line-buffered容易被忽略但很关键。grep默认在处理管道输出时会做缓冲(buffer),等缓冲区积累到一定数据量才会往下游输出,这在配合tail -f做实时监控时会导致明显的延迟(看起来像卡住了,实际是数据攒在缓冲区里没及时往后传)。加上--line-buffered参数后,grep 会按行立即输出,配合tail -f才能实现真正的实时监控效果。


六、AI 大模型日志分析实战案例

结合系列主题,给出一个分析 AI 推理服务日志的完整案例。假设 vLLM 或 Ollama 服务的日志格式包含请求耗时、token 数量等信息:

# 示例日志格式(简化):# [2026-06-30T10:15:23] model=qwen2.5-7b latency_ms=850 prompt_tokens=120 completion_tokens=300 status=success# 1. 统计当天推理请求的总数和成功率total=$(grep"2026-06-30"inference.log|wc-l)success=$(grep"2026-06-30"inference.log|grep"status=success"|wc-l)echo"总请求数:$total, 成功率:$(awk"BEGIN{printf\"%.2f%%\",$success/$total*100}")"# 2. 计算平均推理延迟(latency_ms)grep"2026-06-30"inference.log|awk-F'latency_ms=''{split($2,a," "); sum+=a[1]; n++} END {print "平均延迟:", sum/n, "ms"}'# 3. 找出延迟超过 2000ms 的慢请求,用于排查性能问题grep"2026-06-30"inference.log|awk-F'latency_ms=''{split($2,a," "); if(a[1]>2000) print $0}'# 4. 统计每个模型的 token 吞吐量(用于评估 GPU 资源利用率)awk-F'model=| latency_ms=| prompt_tokens=| completion_tokens='\'{split($2,m," "); models[m[1]] += $4+$5} END {for (mod in models) print mod, models[mod], "tokens"}'inference.log

这套命令组合可以在没有专门搭建监控系统(Prometheus+Grafana,后续第 27 篇会讲)之前,快速对 AI 推理服务的运行状况做出基本判断,是排查问题的第一道工具。


七、常见问题解答(FAQ)

❓ Q1:grep、sed、awk 应该怎么选,有没有简单的判断标准?

简单判断:只是"查找"用 grep;需要"替换/删除"文本内容用 sed;需要按字段做统计、计算、复杂逻辑处理用 awk。三者经常组合使用,先用 grep 缩小范围,再用 awk 做精细分析。

❓ Q2:sed 和 awk 都能做替换,该用哪个?

简单的全文替换用 sed 更简洁(一行命令解决);如果替换逻辑依赖字段位置或复杂计算(比如"把第3个字段的值乘以2"),awk 更合适,因为它对字段的处理能力比 sed 强得多。

❓ Q3:处理几个GB的大日志文件,这些工具会不会内存溢出?

不会。grep、sed、awk 都是流式处理工具,逐行读取处理,不会把整个文件加载进内存,这是它们相比"用编程语言写脚本读取整个文件到内存处理"的天然优势,处理任意大小的文件内存占用都基本恒定。

❓ Q4:awk 的内置变量太多记不住,有没有最常用的几个?

最高频的几个:$0(整行)、$1-$N(各字段)、NF(字段总数)、NR(当前行号)、FS(字段分隔符)、OFS(输出字段分隔符)。掌握这几个基本能应对大部分场景,更复杂的功能用到时再查手册即可。

❓ Q5:正则表达式在 grep/sed/awk 里语法一致吗?

不完全一致。grep 默认是基础正则(BRE),加-E后是扩展正则(ERE);sed 默认也是 BRE,加-E-r启用 ERE;awk 默认就支持 ERE 风格的正则,不需要额外参数。建议统一习惯:处理 grep 和 sed 时都加-E参数,这样三个工具的正则语法基本就统一了,减少记忆负担。


本篇小结与系列导航

📌 核心结论:grep、sed、awk 是 Linux 文本处理的三大支柱,分别负责查找、替换、结构化统计,三者都是逐行流式处理,可处理任意大小文件而不会内存溢出。grep 配合-A/-B/-C上下文参数是排查 Java 异常堆栈的标准做法,sed 修改文件务必配合-i.bak防止误操作,awk 的字段统计能力(结合 sort/uniq)是日志分析、流量统计、AI 推理性能分析的核心武器,三者通过管道组合能解决绝大多数文本分析需求。

📚 参考资料

  • GNU grep 官方手册
  • GNU sed 官方手册
  • GNU awk 官方手册
  • 正则表达式在线测试工具 regex101

如果本文对你有帮助,请点赞 👍 收藏 ⭐ 支持一下!欢迎在评论区留言交流。

系列标签Linuxgrepsedawk文本处理日志分析正则表达式Java运维