拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Shell 文本处理三剑客:grep sed awk 从入门到实战

引言一次凌晨的告警让我真正认识了“三剑客”那是某电商大促前夜凌晨两点运维群里突然炸出一条告警某核心支付网关的错误日志在 10 分钟内暴涨到 2 万行。值班同事在群里喊了一句——“谁帮我从日志里把超时订单号捞出来”我迷迷糊糊打开服务器面对几个 GB 的error.log心里只有一个念头千万别想着用鼠标滚轮翻。于是三行命令下去grep-cTIMEOUT/var/log/pay/error.log# 先看超时出现了多少次grepTIMEOUT/var/log/pay/error.log|sed-ns/.*orderId\([0-9]*\).*/\1/p|sort|uniq-c|sort-rn|head-20awk$3 5000 {print $1, $5, $7}access.log# 捞出响应超时 5 秒以上的请求不到五分钟问题定位某个下游渠道接口抖动导致大量订单超时重试。这一晚之后我彻底明白了一个道理——在 Linux 服务器上不会用 grep、sed、awk 处理文本就像带着算盘去参加编程竞赛。它们被称为“文本处理三剑客”是运维、后端、数据工程师日常工作中绕不开的基本功。本文将从三剑客的定位差异讲起逐一带你吃透 grep、sed、awk 的核心语法、常用参数、实战案例与避坑指南并在结尾准备了一套练习题帮你巩固。无论你是刚接触 Linux 的新人还是想系统梳理的老手相信都会有所收获。一、三剑客总览各司其职组合无敌在深入细节之前先用一张表格建立全局认知。很多初学者把三者混为一谈实际上它们的分工非常清晰工具核心定位一句话理解典型场景grep文本搜索从文件或数据流中“挑出”包含指定模式的行查日志、过滤关键词、统计出现次数sed流编辑按行对文本进行“查找、替换、删除、插入”等操作批量替换配置、删除空行、提取区间行awk文本分析按字段切分每一行做判断、计算与格式化输出日志统计、报表生成、多列条件筛选打个比方如果文本是一片海滩grep 是金属探测器专门帮你找出埋着“关键字”的位置sed 是推土机可以成片地替换、删除、重塑地形awk 则是精密的筛分仪能把沙粒按大小、重量分类统计最终生成报表。三者还有一个重要共性都基于“行”处理都支持从标准输入读取数据因此可以无缝衔接管道|形成威力巨大的组合拳。后面我们会看到大量组合示例。二、grep文本搜索的利器2.1 grep 是什么grep的全称是Global Regular Expression Print即“全局正则表达式打印”。它的职责很简单在文件或标准输入中逐行扫描把包含指定模式的行打印出来。2.2 基本语法grep[选项]模式[文件...]最常用的选项选项含义-i忽略大小写-v反向匹配输出不包含模式的行-n显示行号-c只输出匹配行数不输出内容-r递归搜索目录-E使用扩展正则表达式等价于egrep-o只输出匹配到的部分而不是整行-A n同时输出匹配行之后的 n 行After-B n同时输出匹配行之前的 n 行Before-C n同时输出前后各 n 行Context2.3 基础示例假设有一个测试文件test.txt内容如下Hello World hello linux HELLO grep Goodbye World this is a test line Linux is awesome示例 1忽略大小写查找 hellogrep-ihellotest.txt预期输出Hello World hello linux HELLO grep示例 2显示行号并反向匹配grep-vnWorldtest.txt预期输出2:hello linux 3:HELLO grep 5:this is a test line 6:Linux is awesome这里-v排除了包含World的行-n给每行加上了行号。示例 3统计错误日志中某关键词出现次数grep-cERROR/var/log/application.log预期输出示例值1372.4 进阶用法正则表达式grep 的强大之处在于支持正则表达式。默认使用基础正则BRE加-E后使用扩展正则ERE后者更接近大多数人的书写习惯。示例 4用-E匹配多个关键词逻辑或grep-EERROR|FATALapp.log这行命令会同时匹配包含ERROR或FATAL的行。示例 5用-o只提取 IP 地址grep-oE([0-9]{1,3}\.){3}[0-9]{1,3}access.log|sort|uniq-c|sort-rn|head-5预期含义从访问日志中提取所有 IP统计出现频次按从高到低取前 5 名。这是分析“谁在猛刷接口”的经典操作。示例 6结合-A/-B/-C看上下文当某行报错时只看这一行往往不够需要看它前后的调用链grep-n-C3NullPointerExceptionapp.log预期输出会给出每个匹配行前后各 3 行内容方便追溯异常源头。2.5 注意事项与常见错误忘加引号grep hello world file.txt会被解释为在world和file.txt两个文件中查找hello而不是查找字符串hello world。包含空格或特殊符号的模式必须用引号包裹。正则“贪婪”与特殊字符.、*、[等字符在正则中有特殊含义想匹配字面量需要用\.、\*、\[转义。管道后的 grep 没有行号意义cat file | grep -n xxx中的-n行号是相对“管道流”的如果只取了部分数据行号可能不是原文件行号排查问题时注意区分。grep -r在符号链接目录中的行为递归搜索时某些版本不会自动跟随符号链接需要加-R注意大小写来跟随。三、sed流编辑器3.1 sed 是什么sed是Stream Editor流编辑器的缩写。它把输入流逐行读入“模式空间”根据脚本命令对该行做编辑再把结果输出。默认情况下sed 不会修改原文件而是把结果输出到标准输出只有加上-i选项才会直接改文件。3.2 基本语法sed[选项]命令文件常用选项选项含义-n不自动打印每一行配合p命令精确输出-i直接修改原文件危险操作建议先备份-e允许多个命令-r/-E使用扩展正则表达式常用命令写在单引号内的部分命令含义p打印行d删除行s/旧/新/替换每行替换第一处s/旧/新/g全局替换替换所有匹配处a在匹配行后追加文本i在匹配行前插入文本地址限定可以指定“对哪些行操作”写法含义3p只打印第 3 行1,5d删除第 1 到 5 行/模式/p打印匹配模式的行$最后一行3.3 典型示例沿用上面的test.txtHello World hello linux HELLO grep Goodbye World this is a test line Linux is awesome示例 1删除空行与注释行配置文件里常有讨厌的空行和#注释干扰阅读sed-e/^$/d-e/^#/dnginx.conf预期输出所有空行和以#开头的行都被剔除。示例 2把每行第一处小写 hello 替换为 HIseds/hello/HI/test.txt预期输出Hello World HI linux HELLO grep Goodbye World this is a test line Linux is awesome注意第 1 行是Hello首字母大写没有被替换第 3 行是HELLO全大写也没有被替换。这是因为 sed 默认区分大小写且每行只替换第一处。示例 3全局替换 忽略大小写seds/hello/HI/gItest.txt预期输出HI world HI linux HI grep Goodbye World this is a test line Linux is awesome其中g表示全局替换I表示忽略大小写GNU sed 支持。示例 4区间操作——打印第 2 到第 5 行sed-n2,5ptest.txt预期输出hello linux HELLO grep Goodbye World this is a test line示例 5在匹配行后追加内容sed/Linux/a --- 这是一条追加的分隔线 ---test.txt预期输出在包含Linux的每一行后面加一行分隔线。示例 6批量修改服务器配置实战假设要把一批服务器的 Redis 配置从无密码改为有密码并注释掉旧行# 先备份再替换cpredis.conf redis.conf.baksed-is/^# requirepass.*/requirepass MyStr0ng#Pass/redis.conf这里^# requirepass.*匹配“以 # 开头、后跟 requirepass”的注释行将其替换为正式的requirepass配置。注意-i会直接修改文件所以务必先备份。3.4 注意事项与常见错误-i是一把双刃剑sed -i直接改原文件且不留备份一旦写错正则很难恢复。推荐写法sed -i.bak s/old/new/ file这样会生成file.bak作为备份。分隔符不一定是/当模式或替换内容本身含/时比如路径/usr/local可以用#、|、:等作分隔符例如sed s#/usr/local#/opt#g file避免转义地狱。-n与p的配合很多人只加p不加-n结果每行被打印两次一次是默认输出一次是p命令输出。想要只输出目标行必须-n与p成对出现。sed 默认逐行处理多行之间做替换比如把两行合并成一行比单行替换复杂得多需要用到N命令等高级特性初学阶段不要硬刚。四、awk文本分析与报表生成4.1 awk 是什么awk的名字取自三位创始人Alfred Aho、Peter Weinberger、Brian Kernighan 姓氏的首字母。它是一门模式扫描与处理语言特别擅长把每一行按“字段”切开然后做条件判断、数值计算和格式化输出。如果把 grep 比作“挑选”sed 比作“改写”那么 awk 就是“问询与演说”——你可以一边对数据提问某个字段是否大于阈值一边生成漂亮的报表。4.2 基本语法awk模式 {动作}文件awk 处理每一行时先按分隔符默认空白字符把行切成$1、$2、$3……$0是整行。判断“模式”是否满足满足则执行大括号中的“动作”。模式或动作都可省略省略模式表示每行都执行省略动作只写模式等价于{print $0}。内置变量速查变量含义$0当前整行内容$1,$2,...第 1、2……个字段NF当前行的字段数Number of FieldsNR当前已读的行号Number of RecordsFS输入字段分隔符默认空白OFS输出字段分隔符默认空格RS输入记录分隔符默认换行4.3 典型示例准备一个访问日志片段access.log字段IP、时间、请求方法、状态码、响应耗时毫秒192.168.1.10 10:00:01 GET 200 120 192.168.1.11 10:00:02 POST 500 5200 192.168.1.12 10:00:03 GET 404 80 192.168.1.10 10:00:04 GET 200 3100 192.168.1.11 10:00:05 POST 200 90示例 1打印指定字段只提取 IP 与方法awk{print $1, $3}access.log预期输出192.168.1.10 GET 192.168.1.11 POST 192.168.1.12 GET 192.168.1.10 GET 192.168.1.11 POST示例 2按条件过滤——找出耗时超过 3 秒的请求awk$5 3000 {print $1, $3, $5 ms}access.log预期输出192.168.1.11 POST 5200ms 192.168.1.10 GET 3100ms这里$5是第 5 个字段耗时毫秒条件 3000满足才会打印。示例 3统计与计算——求响应耗时总和与平均值awk{sum $5; count} END {print 总请求数:, count; print 总耗时:, sum ms; printf 平均耗时: %.2f ms\n, sum/count}access.log预期输出总请求数: 5 总耗时: 8590ms 平均耗时: 1718.00 ms这个例子展示了 awk 的BEGIN/END块与变量累加能力。END块在处理完所有行之后执行一次非常适合做汇总统计。示例 4按状态码分组统计类 SQL 的 GROUP BYawk{code[$4]} END {for (c in code) print c, code[c]}access.log|sort-k2-rn预期输出200 3 500 1 404 1这里用关联数组code[$4]记录每个状态码的出现次数最后遍历输出。示例 5自定义分隔符——处理 CSV 文件awk-F,{print $2, $3}users.csv-F,指定逗号为字段分隔符这样就能正确解析 CSV 中的每一列。4.4 注意事项与常见错误$的歧义$1是字段引用而$NF是“最后一个字段”不要把$NF写成$NF之外的自造变量如$last——awk 中变量不带$只有字段引用才带。大括号不能省略动作时却忘了写awk $5 3000是合法的等价于{print $0}但如果逻辑上需要输出特定字段却没写print $1就会整行输出与预期不符。分隔符与正则FS可以设为正则比如-F[,;]表示逗号或分号都算分隔符。但要注意转义复杂场景建议先在小样本上测试。浮点比较awk 内部使用双精度浮点数涉及金额等精确计算时可能有精度误差不适合做金融级精确计算。平台差异macOS 自带的 BSD awk 与 Linux 的 GNU awkgawk在部分函数和选项上有差异脚本迁移时需验证。五、知识扩展正则表达式——三剑客的共同内核无论是 grep 的匹配、sed 的替换还是 awk 的字段处理它们背后都有一个共同的内核正则表达式Regular Expression。理解正则的“编译与匹配”机制能帮你举一反三。5.1 两种流派BRE 与 EREPOSIX 标准定义了两种正则方言BREBasic Regular Expression基础正则、?、|、(、)等元字符默认是字面量要表达“元字符含义”需要加反斜杠转义如\、\?、\|、\(、\)。EREExtended Regular Expression扩展正则上述元字符默认就是元字符写出、?、|就能生效书写更自然。对应到工具上工具默认正则切换到 EREgrepBREgrep -E或egrepsedBREsed -EGNU/sed -rawkERE本身就是扩展正则示例对比想匹配ERROR或FATALgrepERROR\|FATALapp.log# BRE 写法| 需转义grep-EERROR|FATALapp.log# ERE 写法更直观5.2 正则引擎的“回溯”与性能大多数正则引擎采用回溯算法Backtracking。当表达式包含大量量词嵌套时可能触发“灾难性回溯”导致匹配时间指数级爆炸。一个经典反例echoaaaaaaaaaaaaaaaaaaaaaaaaaaaaab|grep-E(a)$在某些实现上这条命令会卡住很久。虽然现代 GNU grep 采用了基于 NFA 的 Thompson 算法避免了灾难性回溯但在 sed、awk 或其他语言中仍可能遇到。因此编写正则时要尽量避免不必要的嵌套量词。5.3 贪婪与懒惰匹配默认情况下*、都是贪婪匹配——尽可能多地吃字符。想改为懒惰匹配需要加?贪婪.* 会匹配 a b 整段 懒惰.*? 只匹配 a 部分不过要注意sed 和 grep 的正则并不支持懒惰量词.*?中的?会被当作普通字符或语法错误这是很多从其他语言转过来的同学容易踩的坑。awk 同样不支持。需要“最短匹配”时往往要改写正则比如用排除字符类[^]*代替.*?grep-oE[^]*test.html# 匹配最短的 ... 标签六、三剑客组合实战三剑客单独使用已经很强组合起来才是真正的生产力。以下给出几个贴近真实工作的组合案例。6.1 查日志快速定位错误上下文grep-n-EERROR|FATALapp.log|sed-n1,20p先把所有错误行带行号抓出来再用 sed 只看前 20 条避免刷屏。6.2 统计访问量 Top 5 的 IPawk sort head 联用awk{print $1}access.log|sort|uniq-c|sort-rn|head-5awk 先抽取每行第一个字段IP再交给sort | uniq -c统计最后排序取前五。更进阶的写法是纯 awk 数组但管道组合更易读。6.3 批量修改多台机器配置sed 脚本化把要执行的替换写进change.sed文件# 注释掉旧的端口号 s/^Port 22/Port 2222/ # 删除空行 /^$/d # 统一 False 为 false忽略大小写 s/False/false/gI然后批量执行forhostinserver{01..20};doscpchange.sed$host:/tmp/ssh$hostsed -i.bak -f /tmp/change.sed /etc/ssh/sshd_configdone6.4 从 Nginx 日志生成 Markdown 报表awk 综合应用awk { status[$4] if ($5 3000) slow total } END { print | 状态码 | 数量 | print |--------|------| for (s in status) printf | %s | %d |\n, s, status[s] print printf - 总请求数%d\n, total printf - 慢请求数3s%d\n, slow }access.log预期输出示例| 状态码 | 数量 | |--------|------| | 200 | 3 | | 500 | 1 | | 404 | 1 | - 总请求数5 - 慢请求数3s2七、最佳实践先备份再-i用sed -i修改文件前先以sed -i.bak形式生成备份或手动cp一份防患未然。小样本先行正则和命令先在head -50 file /tmp/sample的小样本上验证确认无误后再全量执行。善用管道逐步调试不要一上来就写一条超长管道。逐段执行确认每段输出符合预期后再拼接排查效率极大幅提升。给复杂脚本加注释awk 和 sed 的脚本一旦超过 5 行建议写成独立文件-f script.awk/-f script.sed并用#注释说明意图方便日后维护与交接。优先用工具的本职能力要“查”用 grep要“改”用 sed要“算”用 awk。用 awk 做简单替换虽然可以但可读性差不是好实践。别在生产环境“裸奔”大文件上直接跑复杂正则可能吃满 CPU 或内存。优先使用grep -F固定字符串无正则开销处理纯关键词搜索对大文件先考虑LC_ALLC环境下运行可显著提升 grep 性能。八、常见错误汇总常见错误后果正确做法模式/命令不加引号空格或*被 shell 提前展开匹配结果错误用单引号包裹模式和脚本sed -i前不备份替换错误后文件无法恢复使用-i.bak或先cpgrep -r不限定路径扫描范围大、耗时长限定目录或先find过滤awk 中用$变量名取字段理解错误输出异常记住字段固定用$1、$2、$NF混淆与误覆盖重要文件追加用覆盖需再三确认把-n和p拆开用输出重复或漏行sed -n ...p成对使用九、总结与练习9.1 小结本文从三剑客的分工讲起依次深入了 grep 的搜索过滤、sed 的流式编辑、awk 的字段分析并补充了正则表达式底层机制、组合实战、最佳实践与常见错误。核心要点回顾grep负责“找”用-i -v -n -c -E -o -C等选项灵活过滤。sed负责“改”用s///、d、p、a/i等命令逐行编辑-i需慎用。awk负责“算”用$1、NF、NR、BEGIN/END和关联数组完成分组统计与格式化输出。三者都基于正则理解 BRE/ERE 的差异是进阶的关键。9.2 练习题请动手完成以下练习巩固今天的内容。练习 1日志过滤从一个 Web 访问日志中找出所有“状态码为 500 并且请求方法为 POST”的行并显示行号。提示可组合grep -n与正则或用 awk 做多条件判断。练习 2配置清洗编写一条 sed 命令删除某配置文件中的所有空行和以#开头的注释行并直接在原文件上修改记得备份。提示sed -i.bak配合多个-e命令。练习 3统计 IP用一条命令统计出访问日志中出现次数最多的前 3 个 IP。提示awk {print $1} | sort | uniq -c | sort -rn | head -3。练习 4报表生成给定若干行学生成绩数据每行格式为“姓名 语文 数学 英语”请用 awk 计算每名学生的总分与平均分并在最后输出全班的“语文平均分”。提示用数组累加每个学生总分用独立的 sum 变量统计全班语文总分在END块输出。练习 5正则进阶构造一条正则从一个 HTML 文件中提取所有href属性里的 URL 地址要求使用懒惰匹配或排除字符类实现最短匹配。提示grep -oE href[^]*再配合 sed 去掉href前缀和末尾引号。写在最后三剑客的价值不在记忆参数而在“遇到问题先想用什么工具、怎么组合”的思维。建议把这篇文章收藏起来找一台 Linux 机器或 WSL把每个示例亲手敲一遍——当你第一次用一行管道从百万行日志中精准捞出问题的那一秒你会真正理解什么叫“山高路远值得修炼”。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门