Skip to content

了解POSIX扩展正则表达式

本文将简单介绍POSIX扩展正则表达式。

什么是POSIX扩展正则表达式

POSIX扩展正则表达式,也称ERE(Extended Regular Expressions)是基于BRE的扩展功能,目的是为正则表达式提供更加丰富的功能,和更加简洁的写法。

如何启用扩展正则表达式呢?

grep 中,需要使用-E 选项来启用扩展功能,sed 也是同理,需要添加-E来启用扩展,而awk 默认就支持该扩展功能。

新增、修改的点

取消了转义符

ERE中,BRE需要转义的元字符,在ERE中不需要再进行转义了,比如(){} 等。

新增的元字符

ERE中,新增了+?的元字符,其含义是:

  • +: 匹配前面的元素1次或者多次。
  • ?: 匹配前面的元素0次或者1次。

操作

直接引用不需要转义

ERE中,可以直接引用{}() 中,而不需要进行转义,简化了写法,如:

wangli@debian:~$ echo "worrrrld" | grep -E 'r{1,3}'
worrrrld
wangli@debian:~$
wangli@debian:~$ echo "worrrld" | sed -E 's/(r{3})/-\1-/'
wo-rrr-ld
wangli@debian:~$

BRE 中,需要将{}() 进行转义后,才能使用,写出来,可读性大大降低,在ERE中,直接调用即可,可读性增强了。

同样的,如果只想表示{}() 本身的含义,需要用\ 进行转义,如:

wangli@debian:~$ echo "2{35}" | grep -E '2{35}'
wangli@debian:~$
wangli@debian:~$ echo "2{35}" | grep -E '2\{35\}'
2{35}
wangli@debian:~$

新增的元字符

ERE中,新增了?+ 2个元字符,前者表示匹配前面的字符0次或者1次,后者表示匹配前面的字符1次或多次,这其实也是简化了BRE的写法,案例如下:

wangli@debian:~$ echo "1123" | grep -E '111?'
1123
wangli@debian:~$
wangli@debian:~$ echo "1123" | grep "111\{0,1\}"
1123
wangli@debian:~$

这里需要介绍下,为什么1123 可以使用111? 来获取到呢?首先? 的含义是表示匹配前面的字符0次或者1次,所以111? 会有2中情况:

  • 11 ,再匹配一个1 ,结果是111
  • 11,不匹配后面的1 ,结果是11

所以最后会被匹配到,同样的,使用BRE来写的话,表达式为 111\{0,1\},然后功能完全一样,但是非常不直观。

再来看看+ 呢。

wangli@debian:~$ echo "1111111123" | sed -E 's/(1+)/\1-/'
11111111-23
wangli@debian:~$
wangli@debian:~$ echo "1111111123" | sed 's/\(1\{1,\}\)/\1-/'
11111111-23
wangli@debian:~$

通过上面2个语句,可以发现虽然s/(1+)/\1-/s/\(1\{1,\}\)/\1-/ 效果都是一样的,但是第一种要简单、直观很多,可读性要比第二种好。

一些关于正则表达式的例子

剔除多余的空格

有以下文本:

wangli@debian:~$ cat test.txt
            grep    prints

 lines   that    contain   a    match  for one or more            patterns.
wangli@debian:~$

需要将多余的空格给剔除掉。

wangli@debian:~$ cat test.txt | sed -E -e 's/ +/ /g' -e 's/^ +//g'
grep prints

lines that contain a match for one or more patterns.
wangli@debian:~$

使用sed -e 可以执行多条命令,s/ +/ /g 表示将包含了多个连续空格都替换为1个空格,s/^ +//g 表示将以空格开头的连续空格都替换为空,即删除以空格开头的连续空格。

删除注释

定义注释的含义

# 这也是注释
# 这也是一种注释 #
# 这也是一种注释 # 这是正常的内容

有如下内容:

wangli@debian:~$ cat test.txt
# 这也是注释
# 这也是一种注释 #
# 这也是一种注释 # 这是正常的内容

a b e d # 含义是 a b c d
c d e f # 这是注释 # g h i j k

a # 注释 # b

# 这也是注释
# 这还是注释 # ccccc
ddd
aa
wangli@debian:~$

去除掉注释

wangli@debian:~$ cat  test.txt | sed -E -e 's/#.*#//' -e 's/#.*//'


 这是正常的内容

a b e d
c d e f  g h i j k

a  b


 ccccc
ddd
aa
wangli@debian:~$

提取并且交换字符串中的两个数字

有以下文本:

wangli@debian:~$ cat test.txt



a 123 b c d e 456 f
wangli@debian:~$

需要将123456顺序交换。

wangli@debian:~$ cat test.txt | sed -E -n 's/(.* )([0-9]{1,5})( .* )([0-9]{1,5})/\1\4\3\2/p'
a 456 b c d e 123 f
wangli@debian:~$

上面正则含义为:

  • (.* ): 表示匹配以任何字符开头且任何长度的字符,并且以空格结束,并且设为分组1。
  • ([0-9]{1,5}): 表示匹配1-5为数字,并且设置为分组2。
  • ( .* ):表示匹配以空格开头和空格结尾的任何字符,并且设为分组3。
  • ([0-9]{1,5}):表示匹配1-5为数字,并且设置为分组4。

有了如上的规则,就可以将a 123 b c d e 456 f 分为4组,分别为:

  • a
  • 123
  • b c d e
  • 456

此时,在引用分组的时候,只需要将42进行对调,则可以满足123456交换。

获取机器的IP地址

wangli@debian:~$ ip a | sed -E -n 's/.* ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})\/[0-9]{1,3}.*/\1/p'
127.0.0.1
192.168.1.135
wangli@debian:~$

使用ip a 可以查询所有的IP信息

wangli@debian:~$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host noprefixroute
       valid_lft forever preferred_lft forever
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 08:00:27:7c:92:af brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.135/24 brd 192.168.1.255 scope global dynamic noprefixroute enp0s3
       valid_lft 4631sec preferred_lft 4631sec
    inet6 fe80::a00:27ff:fe7c:92af/64 scope link noprefixroute
       valid_lft forever preferred_lft forever
wangli@debian:~$

使用的正则如下:

sed -E -n 's/.* ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})\/[0-9]{1,3}.*/\1/p'

使用sed -E 表示启用正则,而sed -n 表示只输出匹配的部分。

而正则表达式如下:

.* ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})\/[0-9]{1,3}.*

意思为提取包含所有IP地址和子网掩码的内容,并且只提取IP部分,忽略子网掩码,其中IP 部分又规定了获取的是IPv4格式。

所以,该正则表达式含义为:

  • .* :匹配任何长度的字符,并且以空格结束。
  • ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}): 匹配IPv4地址,并且使用分组捕获。
  • \/[0-9]{1,3}.* :匹配斜杠/和子网掩码部分。
  • .*:最后的匹配后面的所有字符。

提取URL中的协议、域名和路径

wangli@debian:~$ echo 'https://1.3.3.4/hello/world.txt' | sed -E -n "s|(.*)://([^/]+)/(.*)|s1:\1 s2:\2 s3:\3|p"
s1:https s2:1.3.3.4 s3:hello/world.txt
wangli@debian:~$

其中正则表达式为:

sed -E -n "s|(.*)://([^/]+)/(.*)|s1:\1 s2:\2 s3:\3|p"

其中[^/]+ 的含义如下:

[] 表示一个字符集,[^/] 则表示除了/ 以外的所有字符,后面的+ 表示上一个字符至少出现1次,这里如果使用(.*)/的话,会出现贪婪匹配。

总结

扩展正则表达式相对基础正则表达式而言,简化了写法,如{}() 不需要在额外转义了,还新增了+? 元字符。

有关正则表达式历史可以看下:正则表达式 - 维基百科,自由的百科全书 (wikipedia.org)




了解POSIX扩展正则表达式

https://wangli2025.github.io/2024/11/08/Extended-Regular-Expressions.html

本站均为原创文章,采用 CC BY-NC-ND 4.0 协议。转载请注明出处,不得用于商业用途。

Comments