正则表达式
基本定义
正则表达式是一种处理字符串的方法,以行为单位来进行字符串的处理行为。通过一些特殊符号的辅助,让用户轻松(?)达到查找、删除、替换某特定字符串的处理程序。
基础正则表达式(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)+$)