正则表达式的原理
简单地讲,正则表达式就是对文本进行过滤的工具。正则表达式之所以拥有过滤文本的功能,是因为它定义了一系列元字符。通过元字符与其它字符的配合来表达一种规则。只有符合此规则的文本才能被保留下来,其余的则会被过滤。
所谓的元字符,是指用来描述字符的字符,元字符的作用在于对字符表达式的内容、转换以及各种操作信息进行描述。而正则表达式就是由各种元字符和一般字符构成的字符串。
基本正则表达式
基本正则表达式(Basic Regular Expression,BRE),又称为标准正则表达式,是最早制订的正则表达式规范,仅支持最基本的元字符集。基本正则表达式是POSIX规范制订的两种正则表达式语法标准之一。基本正则表达式定义的元字符主要有如下几种:
-
行首定位符 “^”
"^"称为行首定位符,是正则表达式中的定位符之一,用来匹配行首的字符,表示行首的字符是"^"后面的那个字符。正则表达式中的定位符的作用与其它元字符不同,它们并不是用来匹配具体的文本,而是匹配某个具体的位置。
# 使用示例:
root@main:/# ls -l | grep ^drw
drwxr-xr-x 2 root root 4096 Jan 9 00:25 bin
drwxr-xr-x 3 root root 4096 Jan 9 00:25 boot
drwxr-xr-x 18 root root 2960 Apr 13 13:29 dev
drwxr-xr-x 78 root root 4096 Apr 28 13:21 etc
drwxr-xr-x 2 root root 4096 Nov 19 23:25 home
drwxr-xr-x 15 root root 4096 Apr 13 13:46 lib
drwxr-xr-x 2 root root 4096 Apr 13 13:46 lib64
drwx------ 2 root root 16384 Jan 9 00:20 lost+found
drwxr-xr-x 3 root root 4096 Jan 9 00:20 media
drwxr-xr-x 2 root root 4096 Jan 9 00:20 mnt
drwxr-xr-x 2 root root 4096 Apr 13 13:29 opt
drwx------ 4 root root 4096 Apr 28 14:28 root
drwxr-xr-x 15 root root 560 Apr 28 14:28 run
drwxr-xr-x 2 root root 4096 Jan 9 00:25 sbin
drwxr-xr-x 2 root root 4096 Jan 9 00:20 srv
drwxrwxrwt 7 root root 4096 Apr 28 14:25 tmp
drwxr-xr-x 10 root root 4096 Jan 9 00:20 usr
drwxr-xr-x 11 root root 4096 Jan 9 00:20 var
在上面这个例子中,使用正则表达式^drw之后将会将以drw开头的行进行输出,其余则舍弃。
-
行尾定位符“$”
与行首定位符的作用恰恰相反,行尾定位符的作用是用来定位文本行的末尾。从语法上讲,行尾定位符的位置也与行首定位符相反,行首定位符位于所作用字符之前,而行尾定位符位于所作用的字符之后。
# 使用示例:
root@main:/# ls -l | grep bin$
drwxr-xr-x 2 root root 4096 Jan 9 00:25 bin
drwxr-xr-x 2 root root 4096 Jan 9 00:25 sbin
在上面这个例子中,系统会将所有根目录下、名字以bin结尾的项目打印出来。
将上述两个定位符号结合使用可实现精准定位:
root@main:/# ls
bin etc initrd.img.old lost+found opt run sys var
boot home lib media proc sbin tmp vmlinuz
dev initrd.img lib64 mnt root srv usr vmlinuz.old
root@main:/# ls | grep ^bin$
bin
-
单个字符匹配“.”
“.”用来匹配任意单个字符,包括空格,但是不包括换行符“\n”。当用户使用“.”符号后,意味着该未知一定有一个字符,无论是什么字符。
例如已经当前目录下存在bzexe与gzexe两个目录,可以使用该匹配符号进行筛选:
root@main:/bin# ls | grep .zexe
bzexe
gzexe
限定符 “*”
星号“*”是正则表达式中的限定符之一。限定符本身不代表任何字符,它是用来指定其前面的一个字符必须重复出现多少次才能满足匹配。“*”表示匹配其签到字符的任意次数,包括0次。
# 使用示例:
root@main:/etc# ls | grep ^sss*
ssh
ssl
在这个例子中 ^sss* 表示匹配的文本必须是以s开头,并且需要连续出现两次。
-
字符集匹配“[]”
方括号“[]”的功能比较特殊,它是用来指定一个字符集合的,其基本语法为:[abc]。其中a、b和c表示任意的单个字符。只要某个字符在方括号所在的位置上出现了方括号中的任意一个字符,就能满足匹配规则。另外们对于连续的数字或者字母,可以使用连字符来表示一个范围,例如“[a-f]”表示匹配字母表中a到f中的任意一个字母,而“[0-9]”表示匹配任意单个数字。
# 使用示例:
root@main:/etc# ls | grep ^rc[0-9]
rc0.d
rc1.d
rc2.d
rc3.d
rc4.d
rc5.d
rc6.d
- 匹配不在指定字符组内的字符"[^]"
作用类似于前者,写法上的区别也仅在于在方括号内部开头处添加一个“^”符号。但功能上的区别在于用于匹配任意一个不属于该字符集的字符。
# 使用示例
[root@localhost check]# echo Tom | grep -e [^T]om
[root@localhost check]# echo Tom | grep -e [^t]om
Tom
- 词首定位符“\<”
匹配以指定字符串开头的单词的所在行。
# 使用示例
[root@localhost home]# echo reset-set | grep '\<set'
reset-set
[root@localhost home]# echo set-reset | grep '\<set'
set-reset
[root@localhost home]# echo test-reset | grep '<\set'
[root@localhost home]# echo reset-test | grep '<\set'
[root@localhost home]# echo testset-reset-test | grep '\<set'
[root@localhost home]#
- 词尾定位符
匹配以指定字符串结尾的单词的所在行。
[root@localhost home]# echo test-reset | grep 'set\>'
test-reset
[root@localhost home]# echo reset-test | grep 'set\>'
[root@localhost home]# echo set-test | grep 'set\>'
set-test
[root@localhost home]# echo setting-test | grep 'set\>'
[root@localhost home]#
扩展正则表达式
扩展正则表达式(Extended Regular Expression,ERE)支持比基本正则表达式更多的元字符,但是扩展正则表达式对某些基本正则表达式所支持的元字符并不支持(不包括前述的几种)。若在grep指令下使用需要加上-E参数或者使用egrep指令,-E参数说明如下:
-E, --extended-regexp PATTERN is an extended regular expression (ERE)
-
限定符“+”
加号“+”的意义与限定符“*”基本相同,但是星号“*”限定前面的字符可以出现任意次,而加号“+”限定前面的字符至少出现一次。
# 使用示例:
root@main:/etc# ls | egrep "^ss+"
ssh
ssl
-
限定符“?”
问号“?”是另外一个限定符,它用来限定前面的字符最多只出现一次,即前面的字符可以重复0次或1次。
# 使用示例:
root@main:/etc# ls | egrep "^ss?"
securetty
security
selinux
services
sgml
shells
skel
ssh
ssl
staff-group-for-usr-local
subgid
subuid
sudoers
sudoers.d
sysctl.conf
sysctl.d
systemd
-
竖线“|”
竖线“|”表示多个正则表达式之间“或”的关系。
# 使用示例:
root@main:/etc# ls | egrep "^ss|^mod"
modprobe.d
modules
modules-load.d
ssh
ssl
Perl正则表达式
正则表达式是Perl语言的一大特色。Shell中的grep和egrep命令都支持Perl正则表达式。Perl正则表达式的元字符与扩展正则表达式中的元字符大致相同。,扩展正则表达式中元字符在Perl正则表达式中都得到了支持。另外,Perl正则表达式还增加了一些元字符。
-
数字匹配\d
符号“\d”匹配0~9中的任意一个数字字符,等价于表达式“[0-9]”。
# 使用示例:
root@main:/etc# ls | grep -P "^rc\d"
rc0.d
rc1.d
rc2.d
rc3.d
rc4.d
rc5.d
rc6.d
-
非数字匹配符\D
符号“\D”和符号“\d”的作用恰好相反,后者会是匹配一个0~9之间的单个数字字符,而前者则匹配一个非数字字符。“\D”等价于表达式“[^0-9]”。
-
空白字符匹配\s
符号“\s”匹配任何空白字符,包括空格、制表符以及换页符等,等价于表达式“[\f\n\r\t\v]”
-
非空白字符匹配\S
符号“\S”匹配任何非空白字符,等价于表达式“[^\f\n\r\t\v]”
- 根据字符重复出现次数匹配
具体形式包括:(1)x\{m\}:字符x重复出现m次;(2)x\{m,\}:字符x至少重复出现m次;(3)x\{\m,n\}:字符x重现出现至少n次但不超过m次。
# 使用示例
[root@localhost check]# echo llllove | grep -P l\{1,2\}
llllove
[root@localhost check]# echo llllove | grep -P l\{10,12\}
[root@localhost check]# echo llllove | grep -P l\{4\}
llllove
[root@localhost check]# echo llllove | grep -P l\{5\}
[root@localhost check]# echo llllove | grep -P l\{1,10\}
llllove
[root@localhost check]# echo llllove | grep -P l\{10,20\}
[root@localhost check]# echo llllove | grep -P l\{3,\}
llllove
正则表达式字符集
在正则表达式中,普通字符集中的字符只表示它们的表面涵义,不对其他字符产生影响。正则表达式的最简单的形式就是只由普通字符集中的字符组成,不包含元字符。正则表达式的字符集通常使用方括号表达式表示,例如:
[cC]hina
[^hello]
[^A-K]
[a-zA-Z]
[0123456789]
[0-9]
第一个表达式表示匹配“china”或者“China”这2个字符串;第2个表达式表示不匹配“hello”这个字符串;第3个表达式表示匹配所有的大小写字母;第4、5个表达式表示匹配所有的个位数字。
# 使用示例:
root@main:/etc# ls | grep "rc[0-9].d"
rc0.d
rc1.d
rc2.d
rc3.d
rc4.d
rc5.d
rc6.d
另还有POSIX字符类(感谢这位朋友的整理):正则表达式——POSIX字符类
在Shell命令中使用正则表达式
grep命令
grep家族由命令grep、egrep和fgrep组成。grep命令在文件中全局查找指定的正则表达式,并且打印所有包含该表达式的行。egrep和fgrep均为grep的变体。egrep是在扩展版的grep,支持更多的正则表达式元字符。fgrep命令被称为固定grep(fixed grep),有时也被称作快速grep(fast grep)。GNU版的grep还提供了一个名为rgrep的递归式grep,用于组成搜索整个目录树。
grep命令的含义
grep命令的名字由来可追溯到ex编辑器。启动ex编辑器后,若需要查找个字符串时,则需要在ex的命令提示符后键入“:/pattern/p”。字符‘p’代表打印(print)命令,所以第一个包含字符串pattern的文本行内容将被打印出来。如果希望打印所有包含pattern的行,可以输入“:/g/pattern/p”,当‘g’出现在pattern前面时,其含义是“文件中所有行”或“执行全局替换”。
又因为匹配的模式被称作正则表达式(regular expression),因此把pattern替换成RE,命令就变成了“:g/RE/p”,这就是grep命令的由来,即“全局查找正则表达式并且打印结果行”。使用grep的好处在于不需要启动编辑器来执行查找,同时也不需要把正则表达式扩在斜杆内。
grep的选项
选项 | 功能 |
-b | 在每一行前面加上其所在的块号,根据上下文定位磁盘块时可能会用到 |
-c | 显示匹配到的行的数目,而不显示行的内容 |
-h | 不显示文件名 |
-i | 比较字符时忽略大小写的区别 |
-I | 只列出匹配行所在文件的文件名(每个文件名只列出一次),文件名之间使用换行符分隔 |
-v | 反向查找 |
-w | 用于进行精确匹配 |
grep的退出状态
grep命令总会返回一个退出状态用来说明能否定位到要查找的模式或文件。如果模式被找到,grep返回的退出状态码为0,表示成功;如果找不到模式,grep返回1作为退出状态码;若找不到目标文件时,grep返回的退出状态是2。
fgrep命令
fgrep命令的运行方式与grep类似,但它不对任何正则表达式元字符做特殊处理。所有字符都只代表它们自己:美元符号就是美元符号、反斜杆就是反斜杆等等。详见如下示例:
[root@localhost home]# echo exp | fgrep ex
exp
[root@localhost home]# echo exp | fgrep e
exp
[root@localhost home]# echo exp | fgrep ^e
[root@localhost home]# echo exp | grep ^e
exp
sed命令
sed是一个精简的、非交互式的编辑器。它能执行与编辑器vi和ex相同的编辑任务。sed编辑器不提供交互方式,只能在命令行中输入编辑命令、指定文件名,然后在屏幕上察看输出信息。sed编辑器没有破坏性,它不会修改文件,除非使用shell的重定向功能保存输出结果。缺省情况下,所有的输出行均被打印至屏幕上。
sed命令的基本格式为
sed 'command' filename
sed命令的定址
用户可以通过定址来绝对对那些行进行编辑。地址的形式可以是数字、正则表达式或者二者的组合。如果没有指定地址,sed就会输入文件中所有的行。如果没有指定地址,sed就会处理输入文件中的所有行。
当所给的地址是一个数字时,这个数字就代表了一个行号。美元符号可用来指代输入文件的最后一行。如果给出的是逗号分隔的两个行号,则要处理的地址就是这两行之间的范围,包括这两行本身。
而sed命令告诉sed对行进行什么操作,包括打印、删除、修改等。
sed的命令与选项
sed命令与扩展正则表达式
在sed命令中使用扩展正则表达式的方法为通过-E或者-r参数。sed --help中的说明如下:
-r, --regexp-extended
在脚本中使用扩展正则表达式
# 使用示例:
[root@FOSDEV build]# echo home/abc/abcdef | sed -E "s/\S+abc\///g"
abcdef
[root@FOSDEV build]# echo home/abc/abcdef | sed -r "s/\S+abc\///g"
abcdef
此处扩展正则表达式的作用是匹配以“abc/”为结尾的字符串部分,并使用sed命令将其删除。