正则表达式
基本定义
正则表达式是一种处理字符串的方法,以行为单位来进行字符串的处理行为。通过一些特殊符号的辅助,让用户轻松(?)达到查找、删除、替换某特定字符串的处理程序。
基础正则表达式(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] 中不是元字符。
 匹配任意子表达式 |
要表达或的意思用元字符竖线 |,表达式用小括号 () 括起来。
例如匹配出 pwpolicy 为 root 和 user 和 luks 用户的配置:
[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)+$)