正则表达式是文本处理的核心工具,掌握 grep、sed、awk 的 regex 语法能大幅提升效率。
一、TL;DR:按场景选工具
- 查找匹配行:
grep -E 'pattern' file,ERE 语法最常用。 - 替换文本:
sed -E 's/old/new/g' file,捕获组用\1引用。 - 字段处理:
awk '/pattern/ {print $1}',内置 match() 和 gsub()。 - BRE vs ERE:BRE 中
+?{}()|需加反斜杠转义;ERE 直接使用 [1]。 - PCRE:
grep -P支持先行断言、命名分组等高级特性,但可移植性较差 [1]。
二、正则基础语法:元字符与量词
.匹配任意单个字符(换行除外)。*匹配前一个元素 0 次或多次(贪婪)。+匹配前一个元素 1 次或多次(ERE/PCRE,BRE 中写\+)。?匹配前一个元素 0 次或 1 次(ERE/PCRE,BRE 中写\?)。{n}/{n,m}/{n,}精确次数、范围、至少 n 次。|交替匹配(ERE/PCRE,BRE 中写\|)。()分组(ERE/PCRE,BRE 中写\(\))。[]字符类,[^]取反。^/$行首 / 行尾锚点。\b/\B单词边界 / 非单词边界(GNU 扩展)。\w/\W/\d/\D/\s/\S(GNU 扩展)。
字符类 [3]:
[[:digit:]]数字,[[:alpha:]]字母,[[:space:]]空白。[[:alnum:]]字母数字,[[:punct:]]标点,[[:upper:]]大写。- 范围
[a-z][0-9],但 locale 影响排序,建议用字符类。
BRE 与 ERE 差异 [1]:
| 语法 | BRE(默认 grep) | ERE(grep -E) | ||
|---|---|---|---|---|
| 量词 + | \+ |
+ |
||
| 量词 ? | \? |
? |
||
| 分组 | \(\) |
() |
||
| 交替 | `\ | ` | ` | ` |
| 次数 | \{n,m\} |
{n,m} |
GNU grep 中 BRE 和 ERE 功能相同,只是符号写法不同。其他实现中 BRE 通常弱于 ERE。
三、grep:查找匹配行
grep -E 'pattern' file.txt
grep -P '(?<=prefix)pattern' file.txt
grep -i -n -v -w -o 'pattern' file.txt
-E使用 ERE 语法 [1]。-P使用 PCRE 语法 [1],支持先行断言(?=...)、后行断言(?<=...)、命名分组(?<name>...)。-i忽略大小写,-n显示行号,-v反向匹配,-w全词匹配,-o仅输出匹配部分。-r递归搜索目录,-l仅列出文件名。
实测输出:
$ echo -e "test123\ntest\ntesting" > /tmp/test.txt
$ grep -E 'test[0-9]+' /tmp/test.txt
test123
$ grep -P '(?<=test)\d+' /tmp/test.txt
test123
$ grep -w 'test' /tmp/test.txt
test
四、sed:替换与转换
sed -E 's/old/new/g' file.txt
sed -E 's/(group1)(group2)/\2-\1/' file.txt
sed -n '/pattern/p' file.txt
s/old/new/替换第一个匹配 [2]。g标志全局替换所有匹配。-E使用 ERE 语法 [2],-r是等价的旧写法。\1\2引用捕获组 [2]。-n配合p命令仅输出匹配行。d删除匹配行,i插入,a追加。
实测输出:
$ echo "room 101 and room 202" | sed -E 's/[0-9]+/NUM/g'
room NUM and room NUM
$ echo "test123" | sed -E 's/(test)([0-9]+)/\1[\2]/'
test[123]
五、awk:字段处理与正则匹配
awk '/pattern/ {print $1}' file.txt
awk 'match($0, /regex/) {print substr($0, RSTART, RLENGTH)}' file.txt
awk '{gsub(/old/, "new"); print}' file.txt
/pattern/作为条件,匹配行执行后续动作 [4]。match(s, r)返回匹配位置,设置RSTART和RLENGTH[4]。gsub(r, s)全局替换,sub(r, s)替换第一个 [4]。$0是整行,$1$2是字段,FS是字段分隔符。~匹配运算符:$1 ~ /pattern/。
实测输出:
$ echo "price=99.99 USD" | awk 'match($0, /[0-9]+\.[0-9]+/) {print substr($0, RSTART, RLENGTH)}'
99.99
$ echo -e "apple\nbanana" | awk '/^ap/ {print}'
apple
六、回滚、排错与局限性
排错速查
| 现象 | 原因 | 处理 |
|---|---|---|
grep: invalid option |
旧版 grep 不支持 -P | 改用 -E 或安装 PCRE 支持 |
BRE 中 + 不生效 |
BRE 默认不识别 + |
加反斜杠 \+ 或改用 -E |
| sed 替换无效果 | 分隔符与内容冲突 | 换分隔符 s#old#new# [6] |
| awk match 返回 0 | 未匹配到 | 检查正则语法,awk 默认 ERE |
| 中文匹配异常 | locale 设置 | export LC_ALL=C 或 LC_ALL=en_US.UTF-8 |
局限性
- BRE/ERE 在不同实现(BSD grep、macOS sed)间存在差异,脚本应优先使用 POSIX 兼容语法或明确声明 GNU 依赖 [5]。
- PCRE (
grep -P) 依赖 libpcre2,部分最小化容器中不可用 [1]。 - 正则表达式不擅长解析嵌套结构(如 HTML/XML),应使用专用解析器。
- 复杂正则在大文件上性能较差,考虑用 awk 或 perl 优化。
实测记录
- 环境:WSL2 Ubuntu 24.04,内核 5.15。
- GNU grep 3.11:BRE/ERE/PCRE 三种语法均正常,
-P支持先行断言。 - GNU sed 4.9:
-EERE 语法、捕获组\1正常。 - GNU awk 5.2.1:pattern 匹配、match()、gsub() 均正常。
- 测试数据:水果列表、数字字符串、价格字符串。
参考资料
- [1] grep(1) – Linux manual page (man7.org)(grep(1))
- [2] sed(1) – Linux manual page (man7.org)(sed(1))
- [3] regex(7) – Linux manual page (man7.org)(regex(7))
- [4] awk(1) – gawk manual (Debian manpages)(awk(1))
- [5] Regular Expressions (GNU Grep 3.12)(GNU grep 手册)
- [6] Regular Expressions – sed, a stream editor (GNU)(GNU sed 手册)
文中主要命令已在 WSL Ubuntu(24.04)实测并核对其在 Debian 系与通用发行版的文档口径。