Linux-正则表达式

背景

处理文本内容的一大利器就是利用正则表达式,这点在vim、sed、gawk、egrep命令中都是非常重要的知识点,但是不同语言下的正则表达式引擎不同。Linux下有两种引擎的正则表达式:POSIX基础正则表达式引擎(BRE)POSIX拓展正则表达式引擎(ERE)。其中Sed命令默认用的BRE中的一部分,而gawk命令用的是ERE,所以Sed使用的时候会有所限制,而gawk就没有太多的限制,可以直接使用。

就本文而言,BREERE的差别体现在如下标识字符中:?+{}(),这些字符在gawk中直接使用,而在sed中就需要借助其它:\,或者指定参数-r

内容

首先看表示字符的元字符:.,它表示任意一个字符;以及表示字符组的符号:[]

1
2
3
4
echo "shuai" | sed -n -e '/.i/p'

# [abc]: 表示去abc中任意一个字符
echo "shuai" | sed -n -e '/[auv]i/p'

然而上面取区间的表述方式对连续区间的取值不是很友好,所以,就有了如下的方式:

1
2
# 用-连接的两个字符,被[]包裹:表示取这两个字符及其之间的所有字符
echo "shuai" | sed -n -e '/[a-cx-z]i/p'

如果像排除某些字符,则可以借助:^

1
2
# 字符组开头加^表示不取字符组中的内容
echo "shuai" | sed -n -e '/[^a-cx-z]i/p'

然后是文本流的开头和结尾的方式:^$

1
2
3
4
5
6
# ^:匹配一行的开头
# $:匹配一行的结尾
echo "shuai" | sed -n -e '/^shuai$/p'

# 但是^出现在正则表达式的字符之间则表示^
echo "sh^uai" | sed -n -e '/sh^uai/p'

接着就是字符数目的标识:*?+{}

1
2
3
4
5
6
7
8
9
10
11
# *:表示*前的字符/分组重复0到无数次
echo "shuai uuuuu shuai" | sed -n -e '/u*/p'

# ?:表示?前的字符/分组重复0到1次
echo "shuai uuuuu shuai" | sed -n -e '/u\?/p'

# +:表示+前的字符/分组重复1到无数次
echo "shuai uuuuu shuai" | sed -n -e '/u\+/p'

# {n,m}:表示{n,m}前的字符/分组最少重复n,最多重复m次
echo "shuai uuuuu shuai" | sed -n -e '/u\{1,8\}/p'

还有就是分组的标识:(),以及管道命令:|表示或:

1
2
3
4
5
# ():将部分正则表达式表示为一个整体
echo "shuai uuuuu shuai" | sed -n -e '/\(uu\)\{1,8\}/p'

# |:表示正则表达式的或关系
echo "shuai uuuuu shuai" | sed -n -e '/shuai | uuuuu/p'

上面这些特殊字符如何被当作常规字符用在正则匹配中呢?这要看情况而定,对于{}()+?字符如果当字符匹配,则直接用即可,反而是在正则中需要加\来标识它们的特殊用处

1
2
3
4
5
# 匹配字符
echo "shuai {uuuuu} shuai" | sed -n -e '/{u*}/p'

# 用作正则
echo "shuai {uuuuu} shuai" | sed -n -e '/\{u*\}/p'

对于.*[]^$|\,如果当字符匹配则需要借助\,而在正则中做特殊标识则直接使用即可。

1
2
3
4
5
# 匹配字符
echo "shuai \{uuuuu\} shuai" | sed -n -e '/\\{u*\\}/p'

# 用作正则
echo "shuai {uuuuu} shuai" | sed -n -e '/u\{1,9\}/p'

BRE正则中的一大特点是其还提供了特殊的字符组,这个就比较重要了,如下:

字符组 含义 例子
[[:alpha:]] 匹配任意字母字符,无论大小写,不匹配标点符号 sed -n -e "/[[:alpha:]]/p" file
[[:alnum:]] 匹配任意字母字符09,AZ或a~z,不匹配标点符号 sed -n -e "/[[:alnum:]]/p" file
[[:blank:]] 匹配空格或制表符 sed -n -e "/[[:blank:]]/p" file
[[:digit:]] 匹配0~9之间的数字 sed -n -e "/[[:digit:]]/p" file
[[:lower:]] 匹配小写字符a~z sed -n -e "/[[:lower:]]/p" file
[[:print:]] 匹配任意可打印字符 sed -n -e "/[[:print:]]/p" file
[[:punct:]] 匹配标点符号 sed -n -e "/[[:punct:]]/p" file
[[:space:]] 匹配任何空白字符:空格、制表符、CR、FF、VT、NL sed -n -e "/[[:space:]]/p" file
[[:upper:]] 匹配大写字符:A~Z sed -n -e "/[[:upper:]]/p" file

注意之处:[[:alpha:]]之间没有任何的空格,其次[[:alpha:]][[:alnum:]]没看出差别。

最后的最后,有一点需要着重说明,对于gawk,如果需要匹配{n,m}这种格式的次数时,就一定要指定参数:--re-interval,否则它就认不得:

1
echo bt | gawk --re-interval '/b{1}t/{print $1}'