Linux 下用正则,最烦的不是语法本身,而是同一个写法在不同的命令里含义不一样。比如我想匹配 aa 或 abb:
grep 'a\{2,3\}' file # 得加反斜杠grep -E 'a{2,3}' file # 不用加sed 's/a\{2,3\}/X/' file # 又得加
加不加反斜杠、加不加 -E、sed 和 grep 还不一样……每次都得现查。今天把这套东西理清楚,以后不再纠结。
三种正则标准:BRE、ERE、PCRE
Linux 里正则表达式不是只有一种"写法",而是三套标准共存。它们的核心差异就一个:哪些元字符需要转义。
| | | |
|---|
| | | |
| ( ) { } | + ? | | |
| | | |
| grep | grep -E | grep -P |
BRE(Basic Regular Expression)
最基础的标准,grep 和 sed 默认用的就是它。特点:特殊字符当成普通字符,要发挥正则作用得加反斜杠。
直接可用的元字符:
加 \ 才发挥特殊含义的元字符:
\( ... \) 分组\{n,m\} 范围次数\| 或操作
ERE(Extended Regular Expression)
BRE 的升级版,不需要转义特殊字符了。( ) { } | + ? 直接写,不用加反斜杠。还多了一些元字符:
一句话总结:能用 ERE 就尽量用 ERE,少敲反斜杠,少出错。
PCRE(Perl Compatible Regular Expression)
功能最强的标准,支持高级特性:非贪婪匹配 *?、+?、??,零宽断言 (?=...)、(?<=...),命名分组 (?P<name>...),条件表达式,递归模式等。只有 GNU grep 的 -P 支持,其他命令基本用不上 PCRE。
各命令怎么用正则(附测试示例)
grep
默认 BRE,-E 切 ERE,-P 切 PCRE(GNU grep 特有)。
准备一个测试文件:
# 创建测试数据cat > /tmp/test.txt <<'EOF'applebananaaaaaaaaaaababbabbb123abc123EOF
示例 1:匹配 a 出现 2 到 3 次
# BRE:花括号需要转义$ grep 'a\{2,3\}' /tmp/test.txtaaaaaaaaa # 注意:匹配的是前3个a,不是整行# ERE:直接写$ grep -E 'a{2,3}' /tmp/test.txtaaaaaaaaa # 同上
⚠️ a\{2,3\} 匹配的是"连续 2 到 3 个 a",所以 aaaa 里的前 3 个 a 命中,整行显示。如果你要精确匹配"整行只有 2 到 3 个 a",得加上行首行尾锚定:grep -E '^a{2,3}$' /tmp/test.txt。
示例 2:匹配 apple 或 banana(或操作)
# BRE:竖线需要转义$ grep 'apple\|banana' /tmp/test.txtapplebanana# ERE:竖线直接写$ grep -E 'apple|banana' /tmp/test.txtapplebanana
示例 3:PCRE 的非贪婪匹配
$ echo"abbb" | grep -P 'ab+?'# 非贪婪,只匹配"ab"$ echo"abbb" | grep -P 'ab+'# 贪婪,匹配"abbb"
sed
默认 BRE,-r(GNU)或 -E(BSD/macOS)切 ERE。不同系统参数名不同,记 -r 就行。
示例 1:替换 apple 或 banana
# BRE:竖线需要转义$ sed 's/apple\|banana/FRUIT/g' /tmp/test.txt# ERE:竖线直接写$ sed -r 's/apple|banana/FRUIT/g' /tmp/test.txt
示例 2:把 a 出现 2 到 3 次替换为 X
# BRE$ echo"aa aaa aaaa" | sed 's/a\{2,3\}/X/g'X X Xa# ERE$ echo"aa aaa aaaa" | sed -r 's/a{2,3}/X/g'X X Xa
示例 3:提取文件扩展名
$ echo"archive.tar.gz" | sed -r 's/.*\.(.*)/\1/'gz
vim
vim 有自己的正则语法,核心是 magic 级别,决定了哪些字符要转义、哪些不要。
修饰符写在正则最开头,只对当前命令生效。
# \m 默认模式:+ 需要转义/^[0-9]\+$# \v 模式:+ 不需要转义/\v^[0-9]+$# \V 模式:() 是普通文本,适合搜字面量/\Vhello(123)
搜索和替换命令都支持:
:%s/[0-9]\+/NUM/g:%s/\v[0-9]+/NUM/g
如果你想永久默认启用 very magic,可以写入 .vimrc:
set magicnnoremap / /\vvnoremap / /\v
ℹ️ \v 只作用于当前这条命令,下次搜索要重新写。[] 字符集规则在所有 magic 模式下都一致,POSIX 类 [[:digit:]] 通用。
find
-name 用的是 shell 通配符,不是正则。要用正则得用 -regex。
示例 1:找 .txt 结尾的文件
# find -regex 默认是 emacs 风格(类似 BRE)$ find . -regex ".*\.txt$"# 指定 ERE$ find . -regextype posix-extended -regex ".*\.(txt|md)$"
示例 2:找文件名以数字开头的文件
# 先在 /tmp 创建几个测试文件$ touch /tmp/1_file.txt /tmp/2_data.log /tmp/abc.txt$ find /tmp -maxdepth 1 -regextype posix-extended \ -regex '.*/[0-9]+_.*'/tmp/1_file.txt/tmp/2_data.log
ℹ️ find 的 -regex 匹配的是完整路径(含目录前缀),不是单纯的文件名。所以上面示例里写的是 .*/[0-9]+_.*,中间多了一个 /。
awk
awk 默认使用 ERE,最省心,不用考虑转义。
# 准备测试数据$ cat > /tmp/score.txt <<'EOF'张三 85李四 92王五 78赵六 100EOF# 打印分数在 80 到 99 之间的行$ awk '$2 ~ /^[89][0-9]$/ {print}' /tmp/score.txt张三 85李四 92# 打印分数是三位数的行(100)$ awk '$2 ~ /^[0-9]{3}$/ {print}' /tmp/score.txt赵六 100
POSIX 字符类
写正则时经常见到 [[:digit:]] 这种写法——双层方括号,外层是字符集,内层 [:xxx:] 是 POSIX 预定义的字符类。
好处:跨平台兼容,多语言环境下比 [0-9] 更可靠。
不建议硬记住,用的时候来查就行:
| | |
|---|
[[:alnum:]] | [0-9A-Za-z] | |
[[:alpha:]] | [A-Za-z] | |
[[:blank:]] | [ \t] | |
[[:cntrl:]] | [\x00-\x1F\x7F] | |
[[:digit:]] | [0-9] | |
[[:graph:]] | [\x21-\x7E] | |
[[:lower:]] | [a-z] | |
[[:print:]] | [\x20-\x7E] | |
[[:punct:]] | | |
[[:space:]] | [ \t\n\r\f\v] | |
[[:upper:]] | [A-Z] | |
[[:xdigit:]] | [0-9A-Fa-f] | |
测试一下 [[:digit:]] 和 [0-9] 的差异:
$ echo"123" | grep '[[:digit:]]\+'# BRE √$ echo"123" | grep -E '[[:digit:]]+'# ERE √# POSIX 字符类在 BRE 和 ERE 下都能用,兼容性最好
三个最常搞混的场景
1. grep 没加 -E,花括号不生效
# 你以为在匹配 2 到 3 个数字$ echo"12" | grep '[0-9]{2,3}'# 啥也不返回——花括号被当普通字符了!# 正确写法:BRE 需要转义,或者开 ERE$ echo"12" | grep '[0-9]\{2,3\}'# √$ echo"12" | grep -E '[0-9]{2,3}'# √
2. sed 和 grep 转义不一致
# grep -E 不需要转义花括号$ grep -E 'a{2}' file # √# sed 默认 BRE,需要转义$ sed 's/a{2}/X/' file # ✗ 花括号没转义,不起作用$ sed 's/a\{2\}/X/' file # √
3. find -name 和 -regex 不是一回事
$ find . -name "*.txt"# 这是通配符,不是正则$ find . -regex ".*\.txt$"# 这才是正则$ find . -regex "*.txt$"# ✗ 正则里 * 不代表任意字符串!
写在最后
Linux 正则的核心矛盾就一个:不同命令默认的正则标准不同,转义规则跟着变。
记住这几条够了:
grep、sed 默认 BRE,grep -E、awk 默认 ERE- find 用
-regex 才是正则,-name 是通配符
下次写正则不确定要不要转义,直接上 -E 或 \v,少踩坑。
如果你在正则上踩过坑,欢迎留言分享。觉得有用的话点个在看,转发给同样在 Linux 下挣扎的同事。
关注「扶锐随笔」,后续还有更多 Linux 实用技巧。