作为一个在 Linux 命令行里摸爬滚打过的人,太理解正则表达式这种”一看就头大,用起来真香”的技术了。这篇笔记算是我整理的一些心得,希望能帮你跨过正则的门槛。
前言:为什么我们离不开正则?
想象一下:你有一个包含几万行日志的文件,想找出里面所有的 error 或者 ERROR 开头的行,或者精准匹配一个格式像 2023-10-27 14:35:22 的时间戳。手动找?噩梦。
这就是 Regular Expression (正则表达式) 登场的时刻。你可以把它理解为一种模式匹配的”通用语言”。我们用一套特定的符号(元字符)来描述文本的”形状”(pattern),然后让 grep、sed、awk 这些工具去帮你寻找符合这个形状的文本。它不是独立工具,而是内嵌在各种工具里的”查找引擎”。学好正则,文本处理效率直接上一个台阶。
基础元字符 – 构成模式的”字母表”
正则表达式由普通字符(比如 a, b, 1)和元字符(有特殊含义的字符)组成。先来认识几个最基础的。
.: 匹配除换行符外的任意单个字符。比如a.c可以匹配abc,a1c,a c。*: 匹配前面的元素零次或多次。ab*c可以匹配ac(b出现0次),abc,abbc。+: 匹配前面的元素一次或多次(需要-E或-P)。ab+c能匹配abc,abbc,但不能匹配ac。?: 匹配前面的元素零次或一次。colou?r可以匹配color和colour。[]: 字符集合,匹配方括号中的任意一个字符。[0-9]匹配所有数字,[a-zA-Z]匹配所有字母。^: 在[]内部开头表示取反([^0-9]匹配非数字),在外部则匹配行的开头。$: 匹配行的结尾。end$匹配以end结尾的行。
记住这些就能开始构建简单模式了。匹配以 Error 开头的行:^Error。匹配包含数字的行:[0-9]。
在 grep 中”捕捞”目标
grep 可能是你接触正则的第一个命令。核心工作就是从输入中筛选出匹配指定 pattern 的行。
基础用法:BRE (Basic Regular Expression)
默认情况下 grep 使用基本正则(BRE)。一些元字符如 +, ?, {, }, (, ), | 需要用反斜杠转义才有特殊含义,这有点反直觉。
# 找出 log.txt 中包含 'error' 或 'ERROR' 的行(BRE)
grep 'error|ERROR' log.txt
# 匹配 'abbc' 这样 b 出现两次的字符串
grep 'ab{2}c' log.txt
扩展正则:-E (ERE) 更自由
用 -E(或者 egrep)启用扩展正则(ERE)。元字符使用更符合直觉,不需要大量转义。
# ERE 版本:匹配 'error' 或 'ERROR'
grep -E 'error|ERROR' log.txt
# 匹配 'abc' 出现一次或多次
grep -E '(abc)+' log.txt
# 匹配 'color' 或 'colour'
grep -E 'colou?r' colorfile.txt
Perl 正则:-P (PCRE) 的”黑科技”
用 -P 启用 Perl 兼容正则(PCRE)。功能最强大,支持非贪婪匹配和零宽断言,性能通常也更好。
# 用 PCRE 匹配 IP 地址(简化模式)
grep -P 'd{1,3}.d{1,3}.d{1,3}.d{1,3}' access.log
# d 等价于 [0-9],这是 PCRE 的便利
小提示: 刚开始建议多用
-E(ERE),平衡功能和易用性。需要处理复杂模式时再考虑-P(PCRE)。
用 sed 进行模式替换
sed 是”流编辑器”,最常用的功能就是替换(s 命令)。语法:sed 's/原模式/替换字符串/标志' 文件,这里的”原模式”就是正则表达式。
# 将 'foo' 替换为 'bar'(每行只替换第一个匹配项)
sed 's/foo/bar/' file.txt
# 全局替换:标志 g
sed 's/foo/bar/g' file.txt
# 结合正则:将 'colour' 或 'color' 统一替换为 'color'
sed -E 's/colou?r/color/g' file.txt
# 捕获组与反向引用:交换两个单词的位置
echo "hello world" | sed -E 's/(bw+b) (bw+b)/2 1/'
# 输出:world hello
sed 默认也是 BRE,推荐用 -E 启用 ERE。
awk 中的 pattern matching
awk 是一门强大的文本处理语言,模式匹配可以作用于整个行,也可以作用于特定字段(列)。
# 打印包含 'error' 的行
awk '/error/ {print}' logfile.txt
# 打印第一列是数字的行
awk '$1 ~ /^[0-9]+$/ {print $1, $2}' data.txt
# 模式取反:打印不包含 'debug' 的行
awk '!/debug/ {print}' logfile.txt
# 逻辑运算:第一列大于100 且 第二列匹配 'pass'
awk '$1 > 100 && $2 ~ /pass/ {print}' results.txt
# match 函数 + RSTART/RLENGTH
echo "abc 123 def 456" | awk '{
if (match($0, /[0-9]+/)) {
printf "Found: %s at pos %dn", substr($0, RSTART, RLENGTH), RSTART
}
}'
# 输出:Found: 123 at pos 5
awk 的正则支持 ERE,~(匹配)和 !~(不匹配)运算符用起来非常直观。
贪婪 vs 非贪婪匹配
默认情况下,正则是贪婪的(greedy)—— 尽可能多地匹配。比如 .* 会吞掉尽可能多的字符。
# 贪婪:匹配从 <b> 到最后一个 </b>
echo '<b>hello</b> world <b>foo</b>' | grep -oP '<b>.*</b>'
# 输出:<b>hello</b> world <b>foo</b>
# 非贪婪(lazy):加 ? 后缀,尽可能少匹配
echo '<b>hello</b> world <b>foo</b>' | grep -oP '<b>.*?</b>'
# 输出:
# <b>hello</b>
# <b>foo</b>
注意: 非贪婪匹配(
*?,+?,??)只在 PCRE(grep -P)中有效。BRE 和 ERE 不支持这个特性。
实用技巧和常见坑
- 引号问题:在 shell 中使用正则时,单引号包裹 pattern 最安全。双引号会被 shell 解释
$,!,`等字符。 - 特殊字符转义:要匹配
.,*,+,?,[,],(,),{,},^,$,|,这些字面量,用转义。 - b 词边界:PCRE 支持
b,匹配单词边界。比如berrorb不会匹配errors或terror。 - 性能陷阱:避免
.*.*这种嵌套通配符,容易导致 catastrophic backtracking(灾难性回溯)。 - 调试方法:用
grep -oP 'pattern' file只输出匹配的部分,方便调试 pattern 是否正确。
总结
正则表达式的学习曲线确实陡,但一旦掌握了基础元字符和 grep/sed/awk 三个工具的配合,你会发现自己处理文本的能力完全上了一个层次。建议从 grep -E 开始练手,遇到复杂需求再上 -P。多练,多踩坑,正则这东西就是熟能生巧。