文本三剑客:grep、sed、awk 的实战组合
为什么是这三件套
Linux 上的文本处理有无数工具,但真正需要肌肉记忆的只有三个:
| 工具 |
职责 |
一句话记忆 |
| grep |
行过滤 |
我要哪些行 |
| sed |
行改写 |
这行要变成什么样 |
| awk |
字段聚合 |
把行拆开算一算 |
它们天然形成流水线:先用 grep 缩小范围,再用 awk 提取字段,最后用 sed 做格式化。
grep:先做减法
最常用的其实是把范围压到最小。
# 递归搜索并显示行号,排除日志与二进制
grep -rn --exclude-dir={.git,node_modules} --binary-files=without-match "TODO" .
# 只看上下文,排障时非常有用
grep -n -C 3 "OutOfMemory" /var/log/app/error.log
# 反向匹配 + 多模式
grep -vE "^(#|$)" /etc/nginx/nginx.conf
一个容易被忽略的点:grep -c 只输出计数,比 grep | wc -l 少一次进程创建,在循环里差距明显。
awk:做结构化提取
awk 的心智模型是「按行读取 → 按字段处理 → 匹配模式 → 执行动作」。
# 统计 Nginx 访问日志中每个 IP 的请求数,取 Top 10
awk '{print $1}' /var/log/nginx/access.log \
| sort | uniq -c | sort -rn | head -10
# 统计各状态码占比
awk '{codes[$9]++} END {for (c in codes) printf "%s\t%d\n", c, codes[c]}' access.log
# 求平均值与最大值
awk '{sum+=$1; if ($1>max) max=$1} END {printf "avg=%.2f max=%d\n", sum/NR, max}' nums.txt
内置变量的价值
| 变量 |
含义 |
NR |
当前行号(跨文件累计) |
NF |
当前行的字段数 |
$NF |
最后一个字段 |
FS / OFS |
输入 / 输出分隔符 |
$NF 在解析不定长日志时特别有用——比如取出「路径」这类总在末尾的字段,不必关心前面有多少列。
sed:做流式改写
# 原地替换,先备份再改
sed -i.bak 's#listen 80;#listen 8080;#g' nginx.conf
# 只处理匹配行之间的区间
sed -n '/BEGIN/,/END/p' config.txt
# 删除空行与注释行,输出有效配置
sed -E '/^\s*(#|$)/d' app.conf
新手最容易踩的坑是 sed -i 没有备份。生产环境建议统一用 -i.bak,改完确认无误再删备份。
把三者串成一条流水线
需求:找出最近 1 小时内出现次数最多的 5 个错误类型。
awk -v since="$(date -d '1 hour ago' '+%H:%M:%S')" \
-F'[][]' '$2 >= since' /var/log/app/error.log \
| grep -oE "ERROR [A-Za-z]+" \
| sed 's/ERROR //' \
| sort | uniq -c | sort -rn | head -5
拆解这段:
awk 负责时间过滤,-F'[][]' 把时间戳从方括号里切出来。
grep -oE 只保留错误类型部分。
sed 去掉前缀噪音。
sort + uniq -c 完成聚合。
小结
- 能用管道组合就不要写脚本,命令行本身的表达力足够强。
- 时间过滤、字段提取、聚合排序分别是 awk / sed / grep 的主场。
- 复杂逻辑超过三行,就该换成 Python 了——可读性比炫技重要。