sed/awk/grep 文本处理速查表 - Linux 文本处理命令大全
日志排障和报表统计几乎都是三段式:grep 捞出关心的行,sed 改掉要改的值,awk 按列汇总。这张表按这三条链路分组,再往外扩出组合管道、sort/uniq/comm 的排序去重与对比、cut/tr/paste 的列与字符处理。定位差异比命令本身重要:grep 是行级筛选,行内任意位置匹配;sed 是流编辑器,逐行替换插入删除,适合格式统一;awk 是列级处理器,只有它能按列累加、分组计数、排序输出。选错工具就要绕远路——统计各 IP 出现次数用 awk 一步到位,用 grep 加 wc -l 则要跑 N 次。判断依据落在两处:大文件时 grep 要加 --line-buffered 否则下游迟迟不出结果,awk 在双引号里必须把 $1 写成 \$1。
典型使用场景
面向做日志排障与报表统计的开发者:grep 定位、sed 改写、awk 按列汇总、sort/uniq 去重计数,四者用管道串成链路,就能覆盖日常八成的文本处理需求。关键是分清三者分工:grep 按行筛,sed 按行改或按行删,awk 按列取数与计算。把它们混用(比如用 grep 去列取数)是新手最常见的效率损失。本表按这五条链路组织,可直接当排错手册用。
grep 匹配与搜索 10
grep "error" app.loggrep -i "error" app.loggrep -rn "TODO" src/grep -v "debug" app.loggrep -c "error" app.loggrep -E "error|warning" app.loggrep -oE "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" access.loggrep -A 3 "error" app.loggrep -B 2 "error" app.loggrep -f pattern.txt app.logsed 替换与编辑 9
sed 's/old/new/g' file.txtsed -i 's/old/new/g' file.txtsed -i.bak 's/old/new/g' file.txtsed '5d' file.txtsed '/^#/d' file.confsed '/^$/d' file.txtsed -n '10,20p' file.txtsed 's/[[:space:]]*$//' file.txtsed -E 's/([0-9]+)/\1/' file.txtawk 提取与统计 9
awk '{print $1}' access.logawk -F: '{print $1, $3}' /etc/passwdawk 'NR==10' file.txtawk 'NR>=10 && NR<=20' file.txtawk '{sum += $1} END {print sum}' nums.txtawk '{count[$1]++} END {for(k in count) print k, count[k]}' access.logawk '$3 > 100' data.txtawk 'length > 80' file.txtawk -F: '$3 >= 1000 {print $1}' /etc/passwd组合管道 Pipeline 4
grep "error" app.log | awk "{print \$1}" | sort | uniq -c | sort -rn | headcat access.log | awk "{print \$1}" | sort | uniq -c | sort -rn | head -20grep -oE "[0-9]+ms" app.log | sort -rn | head -10sed '/^#/d; /^$/d' nginx.confsort / uniq / comm 排序去重与对比 10
sort file.txtsort -n file.txtsort -r file.txtsort -t: -k3 -n /etc/passwdsort -u file.txtsort file.txt | uniq -c | sort -rn | headuniq -c file.txtuniq -d file.txtcomm file1 file2diff -u file1 file2cut / tr / paste 列与字符处理 10
cut -d: -f1 /etc/passwdcut -c1-10 file.txtcut -f1,3 file.txttr 'a-z' 'A-Z' < file.txttr -d ' \t' < file.txttr -s '\n' < file.txtpaste file1.txt file2.txtcolumn -t file.txtexpand file.txtcommand | tee output.txt参数矩阵
| 参数 | 作用 | 示例 |
|---|---|---|
grep -r / -R | 递归搜索;大写 R 额外跟随符号链接 | grep -rn "TODO" src/ |
grep -i / -v | 忽略大小写 / 反向排除 | grep -iv "debug" app.log |
grep -C <n> | 显示匹配行前后各 n 行上下文 | grep -C 3 "error" app.log |
grep -o | 只输出匹配部分而非整行 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' access.log |
sed -i.bak | 原地编辑并留 .bak 备份 | sed -i.bak 's/old/new/g' f.txt |
sed 's/a/b/g' | 全局替换;不加 g 只换每行第一个 | sed 's/localhost/0.0.0.0/g' c.yaml |
sed '/pat/d' | 删除匹配行 | sed '/^#/d' nginx.conf |
awk -F<sep> | 指定字段分隔符 | awk -F: '{print $1, $3}' /etc/passwd |
awk $n / $NF | 第 n 列 / 最后一列 | awk '{print $1, $NF}' f.txt |
awk NR / NF | 当前行号 / 当前行列数 | awk 'NR>=10 && NR<=20' f.txt |
sort | uniq -c | sort -rn | 去重计数并按数值降序 | awk '{print $1}' access.log | sort | uniq -c | sort -rn |
cut -d -f | 按分隔符取固定列 | cut -d: -f1 /etc/passwd |
易错点与避坑指南
现象uniq 去重后结果不对,重复项还在。
原因uniq 只比较相邻行,没有先 sort 让相同内容聚到一起。
处置必须写成 sort file | uniq -c,或用一步到位的 sort -u。
现象sed 改完文件后想撤回,但内容已经变了。
原因用了 sed -i 直接原地编辑且没留备份。
处置改用 sed -i.bak 留 .bak;或先不带 -i 跑一遍确认输出再落盘。
现象grep 明明搜不到实际存在的字符串。
原因大小写不匹配,或模式里的元字符被当正则解释。
处置加 -i;搜字面量用 -F(搜 a.b 会连 axb 一起命中)。
现象用 awk 取到的列全是空的。
原因分隔符不匹配——文件是冒号分隔却没加 -F:,awk 默认按空白切,整行成了一个字段。
处置用 awk -F: 指定分隔符;先用 head 看一眼真实分隔符再写表达式。
现象统计出的 IP 数量不对,每行被算成一次。
原因用 grep -o 提取时漏了管道,或没用 -o 导致 uniq 统计的是整行。
处置grep -oE "模式" file | sort | uniq -c | sort -rn,-o 不能省。
现象sed 替换后文件变成了空。
原因分隔符 / 与模式中的路径冲突,或误用了 d 而非 s。
处置含 / 时改用 # 作分隔符;执行前先不带 -i 看输出。
排障路径
1先看原始数据长什么样再写表达式
head -3 access.log确认分隔符与列的位置,能避免大部分「awk 取不到列」的返工。
2统计访问量 TOP 20
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -20取列 → 排序让相同值相邻 → 计数 → 按数值降序,四步缺一不可。
3带上下文定位报错
grep -n -C 3 "FATAL" app.log-n 给行号、-C 3 前后各三行,通常这几行就说明了问题。
4改文件前先干跑确认
sed 's/old/new/g' config.yaml不加 -i 只输出;确认无误后再用 sed -i.bak 落盘。
5一次清理注释与空行
sed '/^#/d; /^$/d' nginx.conf多条命令用分号分隔,一次扫描完成。
命令示例
统计访问量最高的 20 个 IP
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -20awk 取第一列,sort 让相同值相邻,uniq -c 计数,sort -rn 按数值降序——四步缺一不可,直接 uniq 会因为行不相邻而失效。
输出
1842 203.0.113.7
931 198.51.100.22
407 192.0.2.180只提取 5xx 错误对应的 URL
awk '$9 ~ /^5/ {print $7}' access.log | sort | uniq -c | sort -rn标准 combined 日志格式里第 9 列是状态码、第 7 列是请求路径,所以条件直接落在第 9 列上。比 grep 整行后再切分快得多。
输出
87 /api/orders
34 /api/checkout
12 /api/login删除配置里的注释行与空行
sed '/^#/d; /^$/d' nginx.confsed 的多条命令用分号分隔,可以在一次扫描里完成。配合 -i.bak 就能原地改文件且留有备份。
输出
worker_processes auto;
events {
worker_connections 1024;
}改文件前先干跑一遍确认替换结果
sed 's#http://#https://#g' config.yaml先把 / 换成 # 作分隔符,避免 URL 里的斜杠与 sed 的分隔符冲突。不加 -i 只输出不改文件,确认无误后再加 -i.bak 落盘。
常见坑与注意事项
- sed -i 直接改文件且无备份,改错无法回退;先用不带 -i 的版本确认输出,再用 -i.bak 留后路。
- sed 默认是 BRE,\+ \| \( 在基本正则里不是特殊字符,需要 -r 切到扩展正则。
- grep 搜索含 / 的 URL 路径会与分隔符冲突,改用 # 作分隔符。
- uniq 只比较相邻行,不先 sort 就用会漏掉分散在文件各处的重复项。
- awk 的 $1 从 1 开始,与数组下标习惯不同;$0 是整行,$NF 是最后一列。
- cut 不支持多字符分隔符,也不做任何计算,遇复杂分隔或需统计时应换 awk。
提示
- sed -i 直接修改文件前先备份(-i.bak),或先不加 -i 预览结果再写入。
- grep 处理大文件加 --line-buffered 让输出实时刷新,否则管道下游会等缓冲满。
- awk 单引号内用 $1 取列,双引号内要转义为 \$1,否则 shell 会解释。
- awk -F"|" 把管道符当分隔符统计时,若字段本身可能含 |,改用 -F"[|]" 之外的方案或先 gsub 处理,否则列号会错位。
- sort -u 与 uniq 的区别在于 sort -u 去重的是整行排序后的结果,uniq 只去相邻重复,所以 uniq 前必须先 sort。
- join 要求两个输入文件都按连接字段排序且分隔符一致,先各自 sort -t" " -k2 才能得到正确结果。
常见问题
grep、sed、awk 三者在日志排障里该怎么配合?
典型链路是「先筛后改再汇总」:用 grep -i "error" app.log 把关心的行筛出来,用 sed -n '/^#/d' file 之类的规则先清掉噪声,再用 awk 按列汇总。比如统计 TOP 报错就用 grep "error" app.log | awk "{print $1}" | sort | uniq -c | sort -rn | head。grep 负责定位,sed 负责改写和按行筛选,awk 负责按列取数与统计,三者用管道串起来比分写脚本快得多。
sed -i 原地修改文件太危险,怎么降低改错的代价?
先用 sed -i.bak '/s/old/new/g' file.txt 做原地编辑并自动留下 .bak 备份,确认无误后再删备份;或者先不带 -i 跑一遍 sed 's/old/new/g' file.txt 只看输出,确认替换结果符合预期后再决定是否落盘。这两步习惯能避免绝大多数不可逆的误改。
awk 里 $0、$1、NR、NF 分别代表什么?
$0 是整行内容,$1 $2 是按分隔符切出的第几列($NF 是最后一列),NR 是当前行号,NF 是当前行的列数,它们都是 awk 的内置变量,不需要声明即可使用。例如 awk '{print NR, $1}' file 会同时输出行号和第一列,awk 'NR>=10 && NR<=20' file 则只处理第 10 到 20 行。
为什么 sort -u 和 uniq 要配合 sort 一起用?
uniq 只比较相邻的两行,去重依赖「相同内容已经相邻」这个前提;而 sort 默认把内容排序,同值行自然聚到一起。所以正确写法是 sort file.txt | uniq -c,或用一步到位的 sort -u file.txt。如果不先排序直接 uniq,重复行分散在文件各处就永远识别不出来。
cut 和 awk 都能取列,什么时候用哪个?
cut -d: -f1 /etc/passwd 处理「按单个字符分隔、取固定几列」最快,输出也干净;但它不认多字符分隔符,也不做任何计算。awk -F: '{print $1, $3}' /etc/passwd 更灵活:能设多字符分隔、能按列做条件判断和求和统计。规则很简单——只是取几列用 cut,一旦涉及计算或复杂分隔就用 awk。
由 巧匠 维护
公开更新于 2026年7月21日,内容持续校对官方文档。