做 Linux 运维和开发的兄弟,大概都有过这种经历:服务器跑着跑着日志文件涨到好几个 G,你打开一看,密密麻麻几十万行,想找一条特定的报错信息,眼睛都快看花了,鼠标滚轮滚到手指抽筋还是找不到。或者老板突然甩过来一个 CSV 数据文件,让你"帮忙把第三列的销售额加个总",你打开 Excel 一看,几百万行,直接卡死。
这个时候,如果你还在用 cat 一行一行翻、用 vi 一个个找,那就真的太费劲了。Linux 系统里藏着三个被称作"文本处理三剑客"的命令——grep、sed、awk。它们不是那种花里胡哨的工具,但只要你掌握了,处理文本的效率能翻上几十倍甚至上百倍。很多运维老鸟处理日志,往往就是一行管道命令搞定,旁边的新人还在手动翻文件。
今天这篇就来好好聊聊这三个命令。不讲那些用不上的冷门参数,就聊实际工作中天天能派上用场的用法。看完之后,你会明白为什么老运维们都对这三个命令爱不释手。
一、grep:文本搜索利器
grep 的全称是 Global Regular Expression Print,名字听起来有点学术,但它的核心功能就一句话:在文件里搜索包含指定内容的行。你告诉它你要找什么,它就把所有匹配的行给你揪出来,就这么简单直接。
1.1 最基础的用法
比如你的服务器上有个 nginx 的访问日志 access.log,现在想知道所有包含 "error" 的行,一行命令就够了:
grep "error" /var/log/nginx/access.log
这条命令会把日志里所有包含 "error" 这个词的行都打印到屏幕上。如果日志文件特别大,grep 的速度也比你想象中快得多,因为它底层做了大量优化,搜索一个几十 MB 的文件也就是眨眼的功夫。
1.2 最实用的几个参数
光会基础搜索还不够,grep 有几个参数是日常工作中几乎天天用到的:
-i(忽略大小写):搜索 "error" 的时候,不区分 Error、ERROR、error,全给你找出来。这在排查日志的时候特别有用,因为你根本不知道日志里是大写还是小写。
grep -i "error" /var/log/syslog
-n(显示行号):在每行搜索结果前面加上行号,这样你就能准确定位到文件的具体位置,方便后续用 vi 或其他编辑器去修改。
grep -n "timeout" app.log
-v(反向匹配):这个参数有点反直觉,但它非常实用。它会排除掉包含指定内容的行,只返回不匹配的行。比如说你想过滤掉日志里所有的注释行(以 # 开头的),就可以用:
grep -v "^#" /etc/nginx/nginx.conf
这条命令会把 nginx 配置文件里所有不以 # 开头的行打印出来,也就是把所有有效配置挑出来,注释全部过滤掉。排查配置问题的时候特别好用。
-r(递归搜索):在一个目录及其所有子目录中搜索,后面跟上你要搜的目录路径。这个在大型项目里找某个函数调用或配置项时是神器:
grep -r "DATABASE_URL" /opt/myproject/
-c(只统计匹配行数):不显示具体的内容,只告诉你有多少行匹配到了。比如你想知道今天日志里有多少条错误:
grep -c "ERROR" /var/log/app/2026-04-29.log
输出就是一个数字,比如 "127",表示今天有 127 条错误日志。做监控和告警的时候经常用到。
1.3 正则表达式:grep 的灵魂
grep 之所以强大,很大程度上是因为它支持正则表达式。几个最常用的正则写法:
^ 表示行首:"^error" 匹配以 error 开头的行
$ 表示行尾:"failed$" 匹配以 failed 结尾的行
. 匹配任意单个字符:"err.r" 可以匹配 error、errar、errer
* 表示前一个字符重复零次或多次:"10*" 可以匹配 1、10、100、1000
[0-9] 匹配任意数字:"IP:[0-9]" 匹配 IP: 后面跟一个数字的行
需要更复杂的正则时,用 -E 参数(也叫 egrep),支持扩展正则,可以写 (A|B) 这样的"或"逻辑:
grep -E "(ERROR|WARN|FATAL)" /var/log/app.log
这条命令会同时搜索 ERROR、WARN、FATAL 三种级别的日志,相当于三条 grep 合并成一条,效率直接翻倍。
二、sed:流编辑器,批量修改不在话下
如果说 grep 是用来"找"的,那么 sed 就是用来"改"的。sed 的全称是 Stream Editor(流编辑器),它可以对文本进行查找、替换、删除、插入等操作,而且不需要打开文件编辑,直接在命令行里完成,这对批量处理来说简直是救星。
2.1 替换:sed 最常用的功能
sed 最核心的功能就是文本替换,语法是这样的:
sed 's/旧内容/新内容/' 文件名
其中 s 代表 substitute(替换)。举个例子,假设你有个配置文件 config.txt,里面所有的 "http" 都要改成 "https":
sed 's/http/https/' config.txt
不过要注意,默认情况下 sed 只替换每行第一次出现的匹配内容。如果一行里有两个 "http",它只会改第一个。想改一行里所有的,需要在末尾加个 g(global)标志:
sed 's/http/https/g' config.txt
还有个新手经常踩的坑:上面这些命令只是在屏幕上显示修改后的结果,并不会真的修改文件。想真正写入文件,有两种方式。第一种是加 -i 参数(in-place,原地修改):
sed -i 's/http/https/g' config.txt
这样文件就被真正修改了。建议在加 -i 之前先不加,看看输出对不对,确认无误了再加 -i 写进去,避免改错了文件找不回来。
2.2 删除行
sed 还能删除指定内容的行,用 d 参数(delete):
sed '/^#/d' config.txt
这条命令会删除所有以 # 开头的注释行。再比如,删除文件中的空行:
sed '/^$/d' config.txt
也可以组合使用,比如同时删除注释行和空行:
sed '/^#/d;/^$/d' config.txt
2.3 指定行号操作
sed 不仅可以按内容匹配,还能按行号来操作。比如你只想修改第 5 行:
sed '5s/old/new/' config.txt
修改第 10 到 20 行:
sed '10,20s/old/new/g' config.txt
删除第 3 行:
sed '3d' config.txt
2.4 插入和追加
在第 3 行之前插入一行新内容:
sed '3i\这是新插入的行' config.txt
在第 3 行之后追加一行:
sed '3a\这是追加的行' config.txt
i 是 insert(之前插入),a 是 append(之后追加)。这两个在处理配置文件时特别好用,比如你要在某个配置项后面自动添加一行新配置,一条命令就搞定,不用打开编辑器慢慢找位置。
三、awk:文本分析大师
三剑客里最强大的要数 awk。如果说 grep 负责搜索、sed 负责编辑,那 awk 就负责分析和处理。awk 把每一行文本按"列"来拆分,你可以对指定的列做任何操作,甚至能写简单的程序逻辑。它其实已经是一门完整的编程语言了。
3.1 基本用法:按列提取
awk 默认以空格或制表符作为分隔符,把一行分成若干个字段,用 $1、$2、$3... 来引用。$0 代表整行。
比如你有个 students.txt 文件,内容是:
张三 85 92 78
李四 90 88 95
王五 72 65 80
想提取所有人的名字(第 1 列):
awk '{print $1}' students.txt
想提取名字和第二科成绩:
awk '{print $1, $3}' students.txt
3.2 条件过滤
awk 可以加条件,只输出满足条件的行。比如想找出第二科成绩大于 85 分的学生:
awk '$3 > 85 {print $1, $3}' students.txt
这比 grep 灵活的地方在于,grep 只能做文本匹配,而 awk 可以做数值比较,这对于处理结构化数据来说是天壤之别。
3.3 内置变量
awk 有几个非常好用的内置变量:
NF:当前行的字段数量(Number of Fields)
NR:当前行的行号(Number of Record)
FS:输入字段分隔符(默认空格)
OFS:输出字段分隔符
比如用 NR 加行号输出:
awk '{print NR, $0}' students.txt
处理 CSV 文件(逗号分隔)时,用 -F 指定分隔符:
awk -F',' '{print $1, $3}' data.csv
3.4 BEGIN 和 END 块
awk 支持 BEGIN 和 END 两个特殊块。BEGIN 在处理任何行之前执行,END 在所有行处理完之后执行。最经典的用法就是做统计汇总:
awk '{sum+=$2} END {print "总分:", sum, "平均分:", sum/NR}' students.txt
这条命令做的事情是:逐行读取文件,把第 2 列的成绩累加到 sum 变量里,等全部读完之后(END 块),打印总分和平均分。就这么一条命令,顶得上写一个十几行的 Python 脚本。处理日志统计的时候更是如鱼得水。
3.5 实战:分析 Nginx 访问日志
awk 最拿手的场景就是分析日志文件。比如 Nginx 的 access.log 每一行大概长这样:
192.168.1.100 - - [29/Apr/2026:10:15:30] "GET /index.html" 200 1024
想统计哪些 IP 访问最频繁:
awk '{count[$1]++} END {for(ip in count) print count[ip], ip}' access.log | sort -rn | head -10
这条命令的逻辑是:用 IP 地址($1)作为键,每出现一次计数器加 1,最后遍历所有 IP 和对应的访问次数,按次数从大到小排序,取前 10 名。这就是运维日常排查"哪个 IP 在狂刷接口"的标准做法。
四、三剑客联手:管道让效率飞起来
单个命令已经很强了,但真正的威力在于把它们用管道(|)串联起来。Linux 的哲学就是"每个工具只做一件事,但做到极致",通过管道把多个工具串起来,就能完成非常复杂的任务。
4.1 grep + awk:先筛选再分析
先用 grep 过滤出感兴趣的行,再用 awk 对结果做分析。比如你想统计日志里所有 ERROR 级别的请求中,各个 URL 的出现次数:
grep "ERROR" access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -5
拆解一下这个管道的每一步:
第 1 步:grep 筛选出包含 ERROR 的所有行
第 2 步:awk 提取第 7 列(URL 路径)
第 3 步:sort 排序,把相同的 URL 排到一起
第 4 步:uniq -c 去重并统计每个 URL 出现的次数
第 5 步:sort -rn 按次数降序排列
第 6 步:head -5 取前 5 名
一行命令,六步流水线,直接告诉你哪几个 URL 的报错最多。比打开 Excel 或者写脚本快了不知道多少倍。
4.2 grep + sed:先筛选再修改
先用 grep 找到需要修改的内容,再用 sed 做替换。比如批量修改配置文件里某个服务的端口号:
grep -n "port=8080" /etc/app/config.ini | sed 's/8080/9090/g'
或者更实用一点:找出日志中特定时间段的记录,把敏感信息(比如 IP 地址)脱敏:
grep "2026-04-29" app.log | sed 's/[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}/xxx.xxx.xxx.xxx/g'
这在给客户或领导发日志分析报告的时候特别有用,既能展示问题,又不会泄露敏感 IP。
4.3 三剑客合体:grep + sed + awk
三个命令一起上能干更狠的事。假设你有一个服务器监控日志,每行格式是这样的:
2026-04-29 10:15:30 server1 CPU:92% MEM:78% DISK:65%
你想找出 CPU 使用率超过 90% 的告警,提取时间和服务器名,并格式化输出:
grep "server1" monitor.log | sed 's/%//g' | awk '$6 > 90 {print $1, $2, $3, "CPU告警:", $6}'
这条命令的流程:grep 先过滤出 server1 的日志行,sed 去掉百分号(不然没法做数值比较),awk 判断 CPU 列(第 6 列)是否超过 90,满足条件就打印时间、服务器名和告警信息。结果可能长这样:
2026-04-29 10:15:30 server1 CPU告警: 92
4.4 进阶实战:日志分析一键脚本
再来看一个更贴近实战的例子。假设你的系统日志如下:
2026-04-29 10:15:30 ERROR Connection timeout from 192.168.1.50 port 8080
2026-04-29 10:15:31 INFO Request completed from 192.168.1.100 port 443
2026-04-29 10:15:32 WARN Disk usage at 85% on server2
2026-04-29 10:15:33 ERROR Connection timeout from 192.168.1.50 port 8080
要求:统计 ERROR 日志中,每个来源 IP 的超时次数,并按次数排序:
grep "ERROR.*timeout" system.log | awk '{print $8}' | sort | uniq -c | sort -rn
输出结果类似:
2 192.168.1.50
就这么一行命令,从几十万的日志中精准定位到问题源头——192.168.1.50 这个 IP 出现了 2 次连接超时。排查网络问题的时候,这种分析就是基本功。
五、实用技巧总结
最后分享几个日常用的技巧,能帮你少走很多弯路:
技巧一:用 grep 的 --color=auto 参数高亮匹配内容,搜索结果一目了然:
grep --color=auto "error" app.log
技巧二:sed 修改前一定要先预览!不要一上来就加 -i,先不加 -i 跑一遍看看输出对不对,确认无误了再写回去。这是血泪教训。
技巧三:awk 处理大文件时,如果只需要前几行结果,可以配合 head 使用,避免全文件处理浪费时间:
awk '{print $1}' huge.log | head -20
技巧四:管道命令中,把 grep 换成 awk 来做过滤,有时候能省掉一步。比如 grep "error" file | awk '{print $1}' 可以写成 awk '/error/ {print $1}' file,少开一个进程,性能更好。
技巧五:多个命令串在一起太长记不住?写成 shell 脚本保存起来。下次遇到同样的分析需求,直接执行脚本,几十秒出结果。
六、常见踩坑与避坑指南
在实际工作中用这三个命令,难免会遇到一些坑。下面这些是我踩过之后总结出来的经验,希望能帮你少花点时间调试。
6.1 grep 的正则转义问题
很多新手在用 grep 写正则的时候,发现有些特殊字符不生效。这是因为 grep 默认用的是基本正则(BRE),有些符号需要加反斜杠转义。比如你想匹配 "1 或 2 次重复" 的 \{1,2\},在基本正则需要写成 \\{1,2\\}。如果觉得麻烦,直接用 grep -E 切换到扩展正则(ERE),就不用转义了,写起来更顺手。
另外,grep 搜索的内容如果包含特殊字符(比如点号 .),会被当成正则的"任意字符"来处理。如果你只想匹配字面上的点号,比如搜索 IP 地址中的点,可以用 -F 参数让 grep 做固定字符串匹配,不解析正则:
grep -F "192.168.1.100" access.log
6.2 sed 分隔符冲突
sed 的替换语法默认用 / 作为分隔符,但如果你的内容本身就包含 /(比如文件路径),就会出问题:
# 这样会报错,因为 / 冲突了
sed 's/usr/local/opt/homebrew/' config.txt
解决办法是换一个分隔符,sed 允许你用任意字符做分隔符,常用 | 或 #:
sed 's|usr/local|homebrew/Cellar|g' config.txt
这个小技巧在处理路径替换、URL 替换时非常实用。
6.3 awk 字段引用中的变量
awk 里经常需要根据变量来引用不同的列。比如有时候你想打印第 N 列,但 N 是个变量:
awk -v col=3 '{print $col}' data.txt
用 -v 参数把外部变量传进 awk 里,这样就不用拼字符串了,代码也更安全。这在写脚本的时候特别有用。
6.4 处理含空格的字段
awk 默认以空格为分隔符,但如果你的数据字段本身就包含空格(比如 "New York"),就会被拆成两列。这时候可以改用其他分隔符,比如制表符、逗号、分号:
awk -F'\t' '{print $2}' tsv_data.txt
awk -F';' '{print $1, $3}' semicolon_data.txt
如果分隔符不规则,awk 还支持正则表达式作为分隔符:
awk -F'[ ,|]+' '{print $1, $3}' messy_data.txt
这条命令会把空格、逗号、竖线中的任意一种(或连续多个)都当作分隔符,对格式不太规整的数据特别友好。
总结一下
grep、sed、awk 这三个命令,表面上看只是文本处理工具,但实际上它们是 Linux 系统运维的基本功。grep 负责搜索过滤,sed 负责编辑替换,awk 负责分析计算。单个用已经能解决大部分问题,用管道串起来更是无所不能。
很多新手觉得命令行可怕,其实是因为没掌握这几个核心工具。等你习惯了在终端里一行命令搞定别人花半小时手动处理的事情,那种成就感是真的上瘾的。
这篇文章只是入门,每个命令都值得深入研究。比如 awk 的函数、sed 的多行模式匹配、grep 的 Perl 正则,以后我们会继续展开。如果你觉得这篇有帮助,记得点个在看,分享给身边的朋友。
下期预告:Linux 进程管理——top、ps、kill 命令的全面解析,教你快速定位和解决系统性能问题。我们下期见!