正则表达式

基本定义

正则表达式是一种处理字符串的方法,以行为单位来进行字符串的处理行为。通过一些特殊符号的辅助,让用户轻松(?)达到查找、删除、替换某特定字符串的处理程序。

基础正则表达式(BRE)和扩展正则表达式(ERE)的区别在于支持的元字符不同:

  • BRE 支持的元字符:^ $ . * [ ]
  • ERE 支持的元字符:^ $ . * [ ] + ( ) { } ? |

元字符

元字符要代表字符串意思时必须用转义字符\转义,把元字符变成普通字符。

元字符 名称 Unicode 值 作用
. 句点 U+002E 匹配任意字符
\ 反斜线 U+005C 对字符转义
| 竖线符 U+007C 选择操作(或)
^ 脱字符 U+005E 行起始锚位符
$ 美元符 U+0024 行结束锚位符
? 问号 U+003F 匹配零次或一次的量词
* 星号 U+002A 匹配零次或多次的量词
+ 加号 U+002B 匹配一次或多次的量词
[ 左方括号 U+005B 字符组起始
] 右方括号 U+005D 字符组结束
{ 左花括号 U+007B 量词或代码块起始
} 右花括号 U+007D 量词或代码块结束
( 左括号 U+0028 分组起始
) 右括号 U+0029 分组结束

在字符组内部,某些特殊字符会失去其特殊含义,而只表示字面意义。这些特殊字符包括:

  • [ 表示字符组的开始。
  • ] 表示字符组的结束。
  • - 表示字符组中的范围。
  • ^ 表示字符组取反。

例如在文件内容中查找 1.4:

[root@101c7 sdb4m]$ grep -E '1.4' c.cfg 
1.4
1a4
[root@101c7 sdb4m]$ grep -E '1\.4' c.cfg 
1.4

字符简写式

不同类型正则表达式所支持的字符简写式不同,下表用 B、E、P 代表基础、扩展、Perl 的支持状况:

字符简写式 描述 支持情况
\a 报警符 P
\b 匹配一个单词边界,用法等同于单词分界符 B E P
\B 匹配非单词边界,单词中的元素 B E P
\d 数字字符,等同于 [0-9] P
\D 匹配除数字以外的所有字符 P
\f 换页符,等价于 \x0c 和 \cL P
\r 回车符,等价于 \xd 和 \cM P
\n 换行符,等价于 \x0a 和 \cJ P
\s 匹配空格、制表符(\t)、换行符(\n)、回车符(\r) P
\S 匹配所有文本内容,包括中文,不包含空格。等价于 [^\t\n\r] 或 [^\s] P
\t 水平制表符,等价于 \x09 和 \cI P
\v 垂直制表符,等价于 \xb 和 \cK P
\w 匹配英文字母、数字和下划线,等同于 [a-zA-Z0-9_] B E P
\W 匹配任何非 ASCII 字母字符、非数字以及非下划线字符。等同于 [^A-Za-z0-9_] B E P
[\b] 退格字符 P
\cx 匹配由 x 指明的控制字符,代码可参考控制字符表 P
\uxxxx 字符的 Unicode 值,代码可参考控制字符表 P
\dxxx 字符的十进制值 P
\oxxx 字符的八进制值 P
\x xx 字符的十六进制值 P

POSIX 字符类

不同的编码格式下,可能造成正则输出结果不一样,例如 LANG=zh_CN 情况下 a 和 A 的编号是相邻的。

字符类 说明
[:alnum:] 任何一个字母或数字,等价于 [a-zA-Z0-9]
[:alpha:] 任何一个字母,等价于 [a-zA-Z]
[:blank:] 空格或制表符,等价于 [\t ]
[:cntrl:] ASCII 控制字符,ASCII 0 到 31,再加上 ASCII 127
[:digit:] 任何一个数字,等价于 [0-9]
[:graph:] 和 [:print:] 一样,但不包括空格
[:lower:] 任何一个小写字母,等价于 [a-z]
[:print:] 任何一个可打印字符
[:punct:] 既不属于 [:alnum:] 也不属于 [:cntrl:] 的任何一个字符
[:space:] 任何一个空白字符,包括空格,等价于 [^\f\n\r\t\v ]
[:upper:] 任何一个大写字母,等价于 [A-Z]
[:xdigit:] 任何一个十六进制数字,等价于 [a-fA-F0-9]

控制字符表

对照表如下:

控制字符 Unicode 值 简写 名称
c@* U+0000 NUL 空字符
\cA U+0001 SOH 标题起始
\cB U+0002 STX 文本起始
\cC U+0003 ETX 文本结束
\cD U+0004 EOT 传输结束
\cE U+0005 ENQ 询问
\cF U+0006 ACK 确认
\cG U+0007 BEL 报警符
\cH U+0008 BS 退格符
\cI U+0009 HT 水平制表符
\cJ U+000A LF 换行符
\cK U+000B VT 垂直制表符
\cL U+000C FF 换页
\cM U+000D CR 回车符
\cN U+000E SO 移出
\cO U+000F SI 移入
\cP U+0010 DLE 数据链转义
\cQ U+0011 DC1 设备控制符一
\cR U+0012 DC2 设备控制符二
\cS U+0013 DC3 设备控制符三
\cT U+0014 DC4 设备控制符四
\cU U+0015 NAK 否定性确认
\cV U+0016 SYN 同步空闲
\cW U+0017 ETB 传输块结尾
\cX U+0018 CAN 取消
\cY U+0019 EM 介质结尾
\cZ U+001A SUB 替换
\c[ U+001B ESC 转义
\c\ U+001C FS 信息分隔符四
\c] U+001D GS 信息分隔符三
\c^ U+001E RS 信息分隔符二
\c_ U+001F US 信息分隔符一

字符组 []

中括号[]表示字符组,里面可以列出多个字符或字符范围,用来匹配其中任意一个字符。例如[aeiou]可以匹配任意一个元音字母,[0-9]可以匹配任意一个数字字符。还可以用[^...]的形式表示反向字符组,匹配除了括号内列出的字符以外的任意一个字符。

字符组范围 [ - ]

在字符组内部连字符 - 表示一个范围,只有在字符组内部才是元字符,因此 - 不需要转义。

如果 -[] 内第一个字符,表明匹配 - 的本意,而不是作为连接符用。

例如匹配数字和 %

[root@101c7 sdb4m]$ grep '[0-9%]' a.cfg 
auth --enableshadow --passalgo=sha512
lang en_US.UTF-8
%packages
%end

排除型字符组 [^]

[^ ]列出不希望匹配的字符,比如[^x]表示匹配一个不等于 x 的字符。

并不能匹配空字符。

匹配条件

正则表达式中的匹配条件指的是匹配某个模式的限定条件。

匹配任意字符 .

元字符点号 . 用来匹配除换行符之外任意一个字符,在字符组内部比如 [1.3] 中不是元字符。

匹配任意子表达式 |

要表达或的意思用元字符竖线 |,表达式用小括号 () 括起来。

例如匹配出 pwpolicyrootuserluks 用户的配置:

[root@101c7 sdb4m]$ grep -E 'pwpolicy\s(root|user|luks)' a.cfg 
pwpolicy root --minlen=6 --minquality=1 --notstrict --nochanges --notempty
pwpolicy user --minlen=6 --minquality=1 --notstrict --nochanges --emptyok
pwpolicy luks --minlen=6 --minquality=1 --notstrict --nochanges --notempty

行的起始 ^ 和结束 $

脱字符号 ^ 匹配一行的开始处空字符,美元符号 $ 匹配一行的结尾处空字符。

单个 ^ 匹配所有行。

两个字符一起 ^$ 匹配空行。

例如去除配置文件中的空行和以 # 开头的注释行:

[root@101c7 sdb4m]$ grep -v '^$' a.cfg | grep -v '^#'
auth --enableshadow --passalgo=sha512
cdrom
[root@101c7 ~]$ grep -E -v '^$|^#' anaconda-ks.cfg 

单词分界符 <>

\<单a 表示匹配以 “单 a” 开头的单词;

尾a\> 表示匹配以 “尾 a” 结束的单词;

\<单尾\> 表示匹配完整单词 “单尾”。

例如分别搜索出有以 empty 开头或结尾单词所在行:

[root@101c7 sdb4m]$ grep -E '\<empty' a.cfg 
pwpolicy user --minlen=6 --minquality=1 --notstrict --nochanges --emptyok
[root@101c7 sdb4m]$ grep -E 'empty\>' a.cfg 
pwpolicy root --minlen=6 --minquality=1 --notstrict --nochanges --notempty
pwpolicy luks --minlen=6 --minquality=1 --notstrict --nochanges --notempty

匹配次数

正则表达式中的匹配次数指的是匹配某个模式的次数。

可选项元素 ?

用问号 ? 表示可选项,把它加在一个字符后面表示此处容许出现这个字符,不过它的出现并非匹配成功的必要条件,它只作用于之前紧邻的元素,匹配 0 到 1 次。可使用的语法如下:

语法 描述
?? 懒惰匹配零次或一次(可选)
+? 懒惰匹配一次或多次
*? 懒惰匹配零次或多次
{n}? 懒惰匹配 n 次
{n,}? 懒惰匹配 n 次或多次
{m,n}? 懒惰匹配 m 至 n 次(含 n 次)

例如匹配路径\bin\sbin

[root@101c7 sdb4m]$ grep -E '\/s?bin:' b.cfg 
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync

重复单次 + 和多次 *

加号 + 表示之前紧邻的元素匹配 1 次到多次。

星号 * 表示之前紧邻的元素匹配 0 到任意次数。

疑问号 ?,星号 * 和加号 + 统称为量词,因为它们限定了所作用元素的匹配次数。

疑问号 ? 能匹配一个空格,星号 * 能匹配任意多个空格,它们永远不会匹配失败。加号 + 匹配不到会报告失败。

语法如下:

语法 描述
?+ 占有式匹配零次或一次(可选)
++ 占有式匹配一次或多次
*+ 占有式匹配零次或多次
{n}+ 占有式匹配 n 次
{n,}+ 占有式匹配 n 次或更多次
{m,n}+ 占有式匹配 m 至 n 次(含 n 次)

指定匹配次数 {}

大括号内接数字,用来限制找到的字符数量。例如查找 3 位数字的 UID:

[root@101c7 sdb4m]$ grep -E '[0-9]{3}' b.cfg 
games:x:12:100:games:/usr/games:/sbin/nologin

可以通过 {a,b} 来限定范围,例如查找 6 到 8 位数长的号码:

[root@101c7 sdb4m]$ grep -E '[0-9]{6,8}' b.cfg

也可以只写一半区间,例如查找 11 位长度以上的号码:

[root@101c7 sdb4m]$ grep -E '[0-9]{11,}' b.cfg

分组 ()

分组功能可用功能在各种编程语言下调用形式不同。

例如查找 id 和 gid 都为 89 或 99 的用户:

[root@101c7 sdb4m]$ grep -E '((89|99):)\1' b.cfg 
nobody:x:99:99:Nobody:/:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin

注意分组内容一定是用小括号括起来。

非捕获分组 (?:)

在圆括号内以 ?: 开头,这个圆括号内的内容不会被捕获。在 grep 中不可用。

反向引用 \

也叫捕获分组或向后引用,匹配与表达式先前部分匹配的同样文本。需要用括号 () 包含分组。

例如查找 “boo” 或 “Roo”:

[root@101c7 sdb4m]$ grep -E '(b|R)(o)\2' a.cfg 
# Run the Setup Agent on first boot
firstboot --enable
network  --bootproto=dhcp --device=ens33 --onboot=off --ipv6=auto --no-activate
# Root password

正前瞻 (?=)

grep 中用得较少,一般用在编程中排除掉正前瞻内容作为匹配结果处理。例如正则表达式 /bed(?=room)/ 只能匹配子串 “bedroom”,而子串 “room” 并不作为匹配结果返回,以进行后续处理。

例如匹配 “nochanges --”,并要求后面接的参数为 “emptyok”:

[root@101c7 sdb4m]$ grep -P 'nochanges\s\-\-(?=emptyok)' a.cfg 
pwpolicy user --minlen=6 --minquality=1 --notstrict --nochanges --emptyok

反前瞻 (?!)

匹配后面找不到含有给定前瞻模式的内容。

正后顾 (?<=)

正后顾会查看左边的内容,这与正前瞻方向相反。

反后顾 (?<!)

反后顾会查看某个模式在从左至右的文本流的后面没有出现。

示例

一些简单常用的例子。

匹配 Windows 中空行

Windows 中空行是由回车+换行(CR+LR)组成,如果单个 \r\n 匹配一行中末尾替换成空字符会把下一行内容拼上来:

\r\n\r\n

匹配 UNIX 中空行

Linux 系统中结尾只有 LR 换行符,两个换行符匹配一条空行:

\n\n

匹配 HTML 标签

匹配开标签:

<_a-zA-Z*>

匹配开/闭标签:

</?_a-zA-Z*>

表示匹配标签内容,排除标签结束符 >

[^>]

匹配数字千位

即在每三个数字后面添加一个逗号,例如将 1234567 匹配为 1,234,567。这个正则表达式使用了正向肯定零宽断言和正向零宽断言两个技巧。其中 (?<=\d) 表示匹配前面是数字的位置,(?=(\d\d\d)+$) 表示匹配后面是 3 个数字的倍数并且后面没有其他字符的位置。这样,正则表达式会在满足这两个条件的位置上添加逗号。

(?<=\d)(?=(\d\d\d)+$)