Linux 文本处理三剑客:grep、awk、sed 全面学习笔记
一站式掌握文本处理三大件:从入门到生产,再到性能与坑位。
目录
-
快速对比总览(何时用谁)
-
正则与编码小抄(BRE/ERE/PCRE、大小写与多行)
-
grep—— 搜索与过滤- 用法速览与退出码
- 参数总表(GNU 常见 + 兼容性提示)
- 高频场景与示例
-
awk—— 结构化提取、计算与格式化- 语言模型(pattern { action })
- 命令行参数、内置变量、内置函数
- 常见场景与脚本范式
-
sed—— 流编辑(替换、删除、插入、分支、多行)- 命令行参数与脚本语法
- 地址选择与编辑命令全集
- 高频场景与示例
-
组合技:
grep | awk | sed管道实践 -
兼容性与性能优化(GNU vs. BSD/macOS、LC_ALL、递归、并行)
-
练习题与参考答案思路
1) 快速对比总览
| 工具 | 核心定位 | 强项 | 适合用来 | 不太适合 |
|---|---|---|---|---|
| grep | 搜索/过滤 行 | 高速正则匹配、递归过滤、上下文显示 | 在海量文本中找出匹配行、统计匹配数量、标色显示 | 跨行结构化处理、复杂格式化输出 |
| awk | 行→字段 的结构化处理 | 字段切分、聚合计算、条件筛选、格式化输出 | CSV/TSV/日志字段统计、报表、轻脚本 | 大规模跨行替换(交给 sed)、复杂正则构建(可配合 grep) |
| sed | 流式编辑 | 原地(-i)替换、批量规则、地址选择、跨行编辑 | 批量改配置、清洗文本、多条替换规则 | 复杂统计/聚合(交给 awk) |
选择口诀:找谁?grep;算谁?awk;改谁?sed。
2) 正则与编码小抄
- BRE(Basic):传统基础正则(默认
grep -G、sed),如\{m,n\}需要反斜杠。 - ERE(Extended):扩展正则(
grep -E、sed -E/-r、awk默认),+ ? | () {}无需反斜杠。 - PCRE:Perl 正则(
grep -P,有些平台未编译开启),支持前后查找等高级特性。 - 大小写:
-i忽略大小写。更快匹配可设LC_ALL=C(ASCII 语义、提升速度)。 - 多行:
grep -z结合-P的(?s)可跨“行”(以 NUL 为分隔);sed用N/H/G;awk用自定义RS。
兼容提示:macOS 的
sed是 BSD 版,无-r,用-E;grep -P可能不可用。
3) grep —— 搜索与过滤
3.1 用法与退出码
grep [OPTIONS] PATTERN [FILE...]
# 或:
grep [OPTIONS] -e PATTERN ...
- 退出码:
0=找到匹配;1=未匹配;2=错误(如文件不存在、正则错误)。
3.2 参数总表(GNU grep 常见)
✅ 为最常用;🧩 为兼容性或小众;📁 为文件系统相关;🎨 为显示控制
| 选项 | 含义 | 备注 |
|---|---|---|
✅ -e PATTERN |
指定匹配表达式,可多次 | 避免与以 - 开头的模式冲突 |
✅ -f FILE |
从文件读取模式(每行一个) | 适合维护黑/白名单 |
✅ -i, --ignore-case |
忽略大小写 | |
✅ -v, --invert-match |
取反(输出不匹配的行) | 负过滤 |
✅ -w, --word-regexp |
整词匹配 | 以非字母数字边界界定 |
✅ -x, --line-regexp |
整行匹配 | 模式需匹配整行 |
✅ -o, --only-matching |
只打印匹配片段 | 提取值利器 |
✅ -m NUM, --max-count=NUM |
每文件匹配到 NUM 行后停止 | 搭配 -l 很快 |
✅ -q, --quiet |
静默(只看退出码) | 管道/条件判断非常有用 |
✅ -s, --no-messages |
静默错误信息 | 与脚本搭配 |
✅ -n, --line-number |
显示行号 | |
✅ -H, --with-filename |
始终显示文件名 | 默认多文件自动显示 |
✅ -h, --no-filename |
隐藏文件名 | 合并输出时干净 |
✅ -c, --count |
每文件只输出匹配计数 | 常与 sort -nr 组合 |
✅ -l, --files-with-matches |
仅输出有匹配的文件名 | 反之 -L |
✅ -L, --files-without-match |
仅输出无匹配的文件名 | |
✅ -A N, --after-context=N |
显示匹配后 N 行 | |
✅ -B N, --before-context=N |
显示匹配前 N 行 | |
✅ -C N, --context=N |
显示前后 N 行 | |
🎨 --color[=WHEN] |
高亮匹配 | auto/always/never |
🧩 -E, --extended-regexp |
使用 ERE | 等同历史 egrep |
🧩 -F, --fixed-strings |
字面量匹配(不当正则) | 等同历史 fgrep,最快 |
🧩 -G, --basic-regexp |
使用 BRE(默认) | |
🧩 -P, --perl-regexp |
PCRE(前后查找等) | 某些系统不可用 |
📁 -r, --recursive |
递归读取目录 | 不跟随符号链接 |
📁 -R, --dereference-recursive |
递归并跟随符号链接 | |
📁 -d ACTION |
目录处理:read/recurse/skip |
与 -r 控制精细行为 |
📁 --exclude=GLOB |
排除匹配文件 | 递归时常用 |
📁 --include=GLOB |
仅包含匹配文件 | 递归时常用 |
📁 --exclude-dir=GLOB |
排除目录 | 递归时常用 |
🧩 --binary-files=TYPE |
binary/text/without-match |
-a=text,-I=without-match |
🧩 -a, --text |
二进制按文本处理 | |
🧩 -I |
忽略二进制文件 | 不报“Binary file matches” |
🧩 --line-buffered |
行缓冲输出 | 低延迟管道 |
🧩 -b, --byte-offset |
显示字节偏移 | 索引定位 |
🧩 -Z, --null |
文件名后跟 NUL 分隔 | 供 xargs -0 使用 |
🧩 -z, --null-data |
以 NUL 分隔输入“行” | 跨行匹配技巧 |
BSD/macOS
grep不完全等同:--exclude-dir等选项可能缺失,可改用find ... -exec grep ...组合。
3.3 高频场景与示例
(1) 递归查找关键字并显示上下文
grep -RIn --color -C2 "TODO|FIXME" src/
(2) 仅统计各文件中 ERROR 次数
grep -Rhc "^ERROR" logs/ | paste -d: <(find logs -type f | sort) - | sort -t: -k2,2nr | head
(3) 提取匹配片段(只要值)
# 从日志中提取 IPv4
grep -RoE "\b([0-9]{1,3}\.){3}[0-9]{1,3}\b" /var/log | cut -d: -f2 | sort -u
(4) PCRE 跨“行”抓块(如 BEGIN..END)
# -z 将换行视作普通字符;(?s) 让 . 匹配换行
grep -Pzo '(?s)BEGIN.*?END' big.txt | tr '\0' '\n'
(5) 快速判断并按退出码处理
if grep -q "CRITICAL" app.log; then
echo "发现致命错误,立即告警!"
fi
4) awk —— 结构化提取、计算与格式化
4.1 语言模型与基本用法
awk 'pattern { action }' file
# pattern 省略=>匹配所有行;action 省略=>打印当前行
示例:统计第 3 列之和(逗号分隔)
awk -F, '{sum+=$3} END{print sum}' data.csv
4.2 常用命令行参数
| 选项 | 含义 | 备注 |
|---|---|---|
-F FS |
指定输入分隔符(字段分隔) | 如 -F, 处理 CSV |
-v var=val |
预先设置变量 | 如 -v OFS="," |
-f prog.awk |
从文件读取程序 | 便于复用与维护 |
-e 'program' |
直接指定程序片段 | 可多次叠加 |
-- |
结束选项处理 | 后续按参数传给程序 |
GNU awk(gawk)常见扩展选项(不同版本略有差异):
--posix(严格 POSIX 语义)/--traditional(传统兼容)/--lint[=fatal](提示可疑用法)/--sandbox(禁用system()/文件 IO)/--debug(内置调试器)/--profile[=FILE](生成剖析信息)/--dump-variables[=FILE](导出全局变量与函数表)/--pretty-print[=FILE](美化输出程序)。
4.3 内置变量(高频)
| 变量 | 作用 | 示例 |
|---|---|---|
$0 |
当前整行 | print $0 |
$1..$NF |
第 N 个字段 | print $1,$3 |
NF |
当前行字段数 | if (NF<5) print |
NR |
读到的总行号(全局) | NR==1{print "header"} |
FNR |
当前文件内行号 | 多文件时常用 |
FS |
输入分隔符 | 默认空白;可在 BEGIN 设置 |
OFS |
输出分隔符 | 默认空格 |
RS |
记录分隔符(行分隔) | 可设置为空行或自定义分隔 |
ORS |
输出记录分隔符 | 默认换行 |
FILENAME |
当前文件名 | |
ARGC/ARGV |
参数个数/数组 | 脚本参数处理 |
ENVIRON |
环境变量数组 | ENVIRON["HOME"] |
IGNORECASE |
忽略大小写(gawk) | IGNORECASE=1 |
4.4 内置函数小抄
- 数字:
int()sqrt()rand()srand()atan2() - 字符串:
length()index(s,t)substr(s,i[,n])split(s,a[,fs])match(s,r)sub(r,s)gsub(r,s)gensub(r,s,how[,t])(gawk)tolower/ toupper - 数组(gawk):
asort(a[,d])排序到数组;asorti(a[,d])按键排序 - 时间(gawk):
systime()strftime(fmt[,ts])mktime() - IO/系统:
system(cmd)getlineclose()fflush()
4.5 高频场景与示例
(1) 选择列与重排格式
# 取第1、3列,并以逗号输出
awk -F'\t' -vOFS=, '{print $1,$3}' input.tsv > out.csv
(2) 条件筛选 + 聚合
# 只统计状态为 200 的字节数总和(NCSA 日志示例)
awk '$9==200 {sum+=$10} END{print sum}' access.log
(3) 分组聚合(按字段计数 TopN)
awk -F, '{cnt[$2]++} END{for(k in cnt) print cnt[k],k}' data.csv | sort -nr | head
(4) 使用正则匹配字段
awk -F: '$1 ~ /^(sys|daemon)/ {print $1,$3}' /etc/passwd
(5) 自定义记录分隔符(跨行记录)
# 以空行分隔记录(如 RFC-style headers)
awk -v RS='' '{print NR, length($0)}' mail.txt
(6) 生成简易报表(带表头、对齐)
awk -F, 'BEGIN{printf "%-20s %8s\n","City","Pop"}
{printf "%-20s %8d\n", $1, $2}' city.csv
5) sed —— 流编辑
5.1 命令行参数
| 选项 | 含义 | 备注 |
|---|---|---|
-n |
安静模式(不自动打印) | 需要显式 p 打印 |
-e 'script' |
添加一条脚本命令 | 可多次叠加 |
-f script.sed |
从文件读取脚本 | 复杂场景推荐 |
-i[SUFFIX] |
原地修改(可备份后缀) | macOS 需 -i '' 无备份 |
-E / -r |
使用 ERE 扩展正则 | BSD/macOS 用 -E;GNU 两者皆可 |
-s |
将每个文件视作独立流 | 多文件时避免跨文件状态影响 |
-u |
尝试更少缓冲(兼容用途) | GNU sed 多为兼容占位 |
-z |
以 NUL 分隔记录 | 处理含换行的“记录” |
-l N |
l 命令换行宽度 |
sed -n 'l' 可显示不可见字符 |
5.2 地址选择(选中要处理的行)
- 单地址:
N(第 N 行)、$(最后一行)、/regex/(匹配行) - 范围:
addr1,addr2(如10,20//start/,/end//5,$) - 步进:
first~step(如1~2选奇数行) - 取反:
addr!cmd(对不匹配的行执行 cmd)
5.3 编辑命令全集(常用)
| 命令 | 作用 | 示例 |
|---|---|---|
s/regex/repl/flags |
替换 | s/foo/bar/g 全局;s/a/A/2 第二处;I 忽略大小写 |
p |
打印当前模式空间 | 常与 -n 配合只打印命中行 |
d |
删除当前行(读下一行) | 过滤 |
D |
删除第一个换行前的内容并立即重新开始 | 多行编辑利器 |
q [CODE] |
退出 sed |
加速:匹配到即退出 |
Q [CODE] |
立即退出,不打印模式空间 | |
a\ text |
在后追加文本 | a\new line |
i\ text |
在前插入文本 | |
c\ text |
替换选择的整行 | |
y/set1/set2/ |
字符集替换(逐字符) | ROT13 等 |
n |
打印(若非 -n)并读下一行 | 与条件流结合 |
N |
将下一行追加到模式空间(加入换行) | 跨行编辑 |
h/H |
覆盖/追加到保持空间 | 双缓冲技巧 |
g/G |
从保持空间覆盖/追加到模式空间 | |
x |
交换保持空间与模式空间 | |
= |
打印当前行号 | |
r file |
读文件内容并插入(在当前行后) | |
w file |
将当前行写入文件 | |
: label / b label |
创建标签 / 跳转 | t label 为“若有替换则跳转” |
l |
可视化打印(转义不可见字符) | 与 -l 结合换行宽度 |
\在脚本文件中用于续行;交互命令中a\/i\/c\后通常需换行输入正文。
5.4 高频场景与示例
(1) 原地批量替换(带备份)
# GNU/Linux:备份为 .bak;macOS:-i '' 表示不留备份
gnu_sed: sed -i.bak 's/old/new/g' *.conf
mac_sed: sed -i '' 's/old/new/g' *.conf
(2) 只替换匹配行的第一个 IP 为 [MASK]
sed '/ERROR/ s/\b\([0-9]{1,3}\.){3}[0-9]{1,3}\b/[MASK]/' app.log
(3) 删除区块(从 START 到 END)
sed '/^# START/,/^# END/d' config.ini
(4) 跨行合并:把以反斜杠续行的两行拼成一行
# 将结尾为 \ 的行与下一行合并(去掉续行符)
sed -e ':a' -e '/\\$/ N; s/\\\n//; ta' file
(5) 仅打印包含 foo 的行的下一行
sed -n '/foo/{n;p}' file
(6) 复杂脚本文件示例(多规则)
# clean.sed
# 1) 去掉注释与空行
/^[[:space:]]*#/d
/^[[:space:]]*$/d
# 2) 规范等号两侧空白
s/[[:space:]]*=[[:space:]]*/ = /g
# 3) key 全部小写
s/^\([A-Za-z_][A-Za-z0-9_]*\)/\L\1/
运行:sed -f clean.sed input.conf > output.conf
6) 组合技:管道与分工
(1) 先筛后算
# 统计 5xx 状态的请求体字节和
grep -E "\s5[0-9]{2}\s" access.log | awk '{sum+=$10} END{print sum}'
(2) 先改再算
# 将大小写不敏感的 ERROR/WARN 统一为大写再按级别计数
sed -E 's/(error|warn)/\U\1/g' app.log | awk '{cnt[$1]++} END{for(k in cnt)print k,cnt[k]}'
(3) 结构化提取 + 仅值输出
# 提取配置中所有端口号(去重、排序)
grep -RhoE '\bport[[:space:]]*=[[:space:]]*[0-9]+\b' conf/ |
sed -E 's/.*=\s*([0-9]+)/\1/' |
sort -n | uniq
7) 兼容性与性能优化
-
GNU vs. BSD/macOS:
sed -i:GNU 可省后缀,macOS 需-i ''才不留备份。sed -r:GNU 可用,macOS 无此选项,用-E。grep -P:PCRE 在部分系统未编译;可用perl -ne 'print if /.../'代替。- 一些
--exclude*选项在 BSDgrep缺失,用find ... -exec组合。
-
速度优化:
- 确定是字面量匹配:
grep -F更快。 - 降低本地化开销:
LC_ALL=C可显著加速纯 ASCII 文本处理。 - 大目录递归:用
ripgrep (rg)可更快;或xargs -P并行。 awk中减少管道与system()调用;汇总放在END{}。
- 确定是字面量匹配:
-
健壮性:
- 处理“奇怪文件名”用 NUL 分隔:
grep -Z、xargs -0、find -print0。 - 正则含
{}在 BRE 需转义;在 ERE 则不需。 - 批量改文件建议先备份或用
git追踪。
- 处理“奇怪文件名”用 NUL 分隔:
8) 练习题(附思路)
-
统计 NCSA 日志中各 IP 命中数 Top 10
思路:awk '{cnt[$1]++} END{for(k in cnt) print cnt[k],k}' | sort -nr | head -
把
.env文件里KEY=value的 value 去掉两端空白并小写
思路:sed -E 's/^([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/\1=\L\2/' .env -
在源码中查找 TODO,并显示文件名、行号与前后 1 行上下文
思路:grep -RIn -C1 'TODO' src/ -
从 CSV 第 2 列提取域名,统计不同后缀 .com/.net/.org 数量
思路:awk -F, '{split($2,a,"."); t=a[length(a)]; cnt[t]++} END{for(k in cnt)print k,cnt[k]}' file.csv -
合并以反斜杠续行的配置,删除注释与空行
思路:用sed的N; s/\\\n//合并,再用地址与正则删除注释/空白。
附:常见错误排查
sed: RE error: illegal byte sequence:设置LC_ALL=C或统一输入编码。grep: binary file matches:加-a或-I。awk: field separator被正则特殊含义影响:用字符类或转义,如-F'[|]'。- macOS
sed -i报错:需-i ''。
温馨提示:不同发行版/版本参数可能略有差异,以
man grep/man sed/man awk或--help为准。本文覆盖 GNU 常见参数与实践套路,满足日常到生产的大多数需求。

浙公网安备 33010602011771号