文本三剑客:grep / sed / awk
Linux 文本处理绕不开的三个工具。这篇按「能干什么」整理高频用法,配合常用命令速查使用。
grep — 找内容
grep "ERROR" app.log # 基本查找
grep -i "error" app.log # 忽略大小写
grep -n "ERROR" app.log # 显示行号
grep -r "TODO" . # 递归找目录
grep -rn "TODO" --include="*.cpp" . # 递归 + 只搜 .cpp
grep -v "^#" config # 反选:排除注释行
grep -c "ERROR" app.log # 只输出匹配行数
grep -o "[0-9]\{3\}" file # 只输出匹配到的部分
grep -E "regex" file # 扩展正则(+ ? | {n} 不用转义)
grep -A 3 -B 1 "ERROR" app.log # 显示匹配行的后 3 行 / 前 1 行
grep -w "error" file # 全词匹配(不匹配 errors)
grep -F "a.b" file # 按字面量匹配(. 不当正则)
egrep / fgrep
egrep = grep -E,fgrep = grep -F。新脚本里建议直接写 grep -E/-F。
sed — 改内容
# 替换:s/旧/新/标志,每行默认只换第一个
sed 's/foo/bar/' file # 输出到终端,不改文件
sed 's/foo/bar/g' file # g = 替换行内所有
sed 's/foo/bar/2' file # 只替换每行第 2 个
sed -i 's/foo/bar/g' file # 直接修改文件(危险,先备份可加 -i.bak)
sed -i '/^#/d' config # 删除所有注释行
sed -i '/^$/d' config # 删除空行
sed -n '10,20p' file # 只打印 10-20 行
sed -n '/START/,/END/p' file # 打印两个标记之间的区域
sed '1d' file # 删除第一行(去表头)
sed '$a\new line' file # 末尾追加一行;a=后加 i=前插
sed -E 's/[0-9]+/N/g' file # -E 扩展正则(同 grep -E)
sed -i 的坑
macOS(BSD sed)的 -i 必须带备份后缀参数(sed -i '' 's/a/b/'),GNU/Linux 不用。跨平台脚本推荐统一用 sed -i.bak 再删备份,或改用 perl。
awk — 切字段、做统计
# 基本模型:awk '模式 { 动作}' file,默认按空白切字段,$1 $2 ... $NF 是各字段
awk '{print $1}' file # 打印第一列
awk '{print $1, $3}' file # 打印第 1、3 列
awk -F: '{print $1}' /etc/passwd # -F 指定分隔符
awk -F, '{print $2}' data.csv # CSV(简单场景;含引号转义请用专业工具)
awk 'NR==3' file # 只打印第 3 行(NR = 当前行号)
awk 'NR>1' file # 跳过表头
awk 'END{print NR}' file # 总行数
awk '{sum += $1} END{print sum}' file # 第 1 列求和
awk '{sum += $3; n++} END{print sum/n}' f # 第 3 列平均值
awk 'length($0) > 80' file # 打印超过 80 列的行
awk '$3 > 100' file # 条件过滤:第 3 列大于 100
awk '$NF' file # 最后一列(NF = 字段数)
awk '/ERROR/{print $1, $NF}' app.log # 模式 + 动作组合
内置变量
NR 当前行号、NF 当前行字段数、FS 分隔符(可用 -F 或 BEGIN{FS=":"} 设置)、OFS 输出分隔符。
配角:cut / sort / uniq / tr / xargs
cut -d: -f1 /etc/passwd # 按分隔符取列(比 awk 轻,但只支持单字符分隔)
cut -c1-8 file # 按字符位置截取
sort -n / -r / -h # 数值 / 倒序 / 人性化大小(1K 2M) 排序
sort -k2 -n file # 按第 2 列数值排序
uniq -c # 去重并计数(**先 sort 再 uniq**)
tr 'a-z' 'A-Z' < file # 大小写转换
tr -d '\r' < win.txt > unix.txt # 去掉 \r(Windows 换行转 Unix)
xargs -n1 -I{} cmd {} # 把输入变成参数逐个执行
find . -name "*.log" | xargs rm # 找到的文件批量删除
grep -rl "old" . | xargs sed -i 's/old/new/g' # 批量替换(经典组合)
实战组合
# 1. 统计日志里出现次数最多的 10 个 IP(nginx access.log 第 1 列)
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head
# 2. 提取 nginx 日志中所有 404 的请求路径
awk '$9 == 404 {print $7}' access.log | sort | uniq -c | sort -rn
# 3. 找出当前目录占用最大的 10 个文件
du -ah . | sort -rh | head -n 10
# 4. 批量把 .txt 改成 .bak
ls *.txt | sed 's/\.txt$//' | xargs -I{} mv {}.txt {}.bak
# 5. 看 docker 容器里 java 进程的启动参数(jps + tr 组合思路)
ps -ef | grep java | tr ' ' '\n' | grep -E '^-X'
# 6. 快速看 csv 某列非空的行数
awk -F, '$2 != "" {n++} END{print n+0}' data.csv
什么时候换更专业的工具
- JSON →
jq:curl -s api | jq '.data[0].name' - 多层结构 / 跨行匹配 →
perl或python - 超大文件反复查 → 考虑先
grep缩小范围,或ripgrep (rg)(更快的 grep 替代) - CSV 含引号 / 转义 →
csvkit、mlr或 Python pandas