awk模式(Pattern)之二

正则模式

先说说什么是正则模式。

见名知义,"正则模式"肯定与"正则表达式"有关,所以,如果想要使用这种模式,则必须先学会在Linux中使用正则表达式,如果你对正则表达式还不是特别熟悉,可以参考博客中的系列文章:在Linux中使用正则表达式

 

前文中提到过,"模式"可以理解为"条件",当不指定模式时,文本中的每一行都会执行对应的动作,当指定模式时,只有被模式匹配到的、符合条件的行才会执行对应的动作。

那么什么是正则模式呢?正则模式可以理解为,把"正则表达式"当做"条件",能与正则匹配的行,就算满足条件,满足条件的行才会执行对应的动作,不能被正则匹配到的行,则不会执行对应的动作。如果你觉得我说的不明白,来看一个小例子,就能理解。

 

不过在进行示例之前,我们先来思考一个小问题。

我们知道,在Linux中,/etc/passwd文件中存放了用户信息,那么假设 ,我们想要从/etc/passwd文件中找出"用户名以zsy开头"的用户,我们该怎么办呢?

没错,我们可以使用grep命令,配合正则表达式,找出对应的信息,示例如下。

[root@server01 ~]# grep "root" /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin

如上例所示,我们通过grep命令,配合正则表达式,找出了我们需要的信息,那么,使用awk命令,能否完成上述需求呢?

答案是肯定的,那么我们一起来看看,使用awk命令,怎样从/etc/passwd文件中找出用户名以zsy开头的用户,示例如下

[root@server01 ~]# awk '/^root/{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash

不管是使用grep命令,还是使用awk命令,都使用了相同的正则表达式"^zsy"

唯一的区别就是,在grep命令中,直接使用了正则表达式,而在awk命令中,正则表达式被放入了两个斜线中。

这样说可能不容易理解,看图说话似乎更加容易理解。

 

 

 

awk命令在使用正则表达式时,将正则表达式放入了"/    /"中。

其实,这就是我们今天要介绍的"正则模式",在使用"正则模式"时,文本行如果能够被正则表达式匹配到,就会执行对应的动作,如果没有被正则匹配到,则不会执行对应的动作,而上例中,对应的动作就是{print $0},也就是打印整行,所以,上例中的grep命令与awk命令所实现的效果是完全相同的,那么你可能会问,既然效果完全相同,为什么还要使用awk呢?似乎grep更加简单一些,没错,上例中,grep是更加简单一些,但是不要忘了,awk有自己的优势,就是格式化能力,那么,我们换一个场景,可能使用awk就会更加实用了,示例如下。

[root@server01 ~]# awk -F ":" 'BEGIN{printf "%-10s%-10s\n","用户名","用户ID"}/^root/{printf "%-10s\t%-10s\n",$1,$3}' /etc/passwd
用户名       用户ID      
root            0  
[root@server01 ~]# awk -v FS=":" 'BEGIN{printf "%-10s%-10s\n","用户名","用户ID"}/^root/{printf "%-10s\t%-10s\n",$1,$3}' /etc/passwd
用户名       用户ID      
root            0     

猛然一看,上例似乎非常复杂,但是如果你已经掌握了前文中的知识,那么你一定能够看明白,上例中蓝线标注的部分使用了BEGIN模式,并且格式化输出了一行文本作为"表头",上例中红线标注的部分使用了正则模式,并且格式化输出了/etc/passwd文件中的第一列与第三列(用户名字段与用户ID字段),上例中,只使用了awk一条命令就完成了如下多项工作。

1、从/etc/passwd文件中找出符合条件的行(用户名以zsy开头的用户)。

2、找出符合条件的文本行以后,以":"作为分隔符,将文本行分段。

3、取出我们需要的字段,格式化输出。

4、结合BEGIN模式,输出一个格式化以后的文本,提高可读性。

因为我们在处理文本时,往往需要用到正则表达式,所以,awk的正则模式应该会经常用到。

但是需要注意,在使用正则模式时,如果正则中包含"/",则需要进行转义,这样说可能不容易理解,我们来看个例子。

仍然使用/etc/passwd进行测试,我们知道,/etc/passwd中保存了用户信息,其中每行的最后一个字段为用户使用的登录shell,假设,我们想要从passwd文件中找出使用/bin/bash作为登录shell的用户,我们该怎么办呢?

没错,我们可以使用grep命令,配合正则表达式完成我们的需求,示例如下。

[root@server01 ~]# grep "/bin/bash$" /etc/passwd
root:x:0:0:root:/root:/bin/bash

使用"/bin/bash"作为登录shell的用户都被我们找了出来。

同理,我们使用awk命令,同样能够实现与grep相同的效果。

于是,按照套路,你可能会尝试如下命令。

[root@server01 ~]# awk '/\/bin\/bash/{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash

经过转义后,awk命令即可正常的匹配到符合正则条件的行,并执行了相应的动作。

除此之外,还要注意以下两点

1、当在awk命令中使用正则模式时,使用到的正则用法属于"扩展正则表达式"(如果不理解,请参考博客中的"正则表达式"系列文章)。

2、当使用 {x,y} 这种次数匹配的正则表达式时,需要配合--posix选项或者--re-interval选项。

[root@server01 ~]# awk --posix '/he{2,3}p/{print $0}' test3
heep
heeep
[root@server01 ~]# awk --re-interval '/he{2,3}p/{print $0}' test3
heep
heeep

上例中,正则模式中的正则表达式为"he{2,3}y",此表达式表示"hey"中的字母e最少需要连续出现2次,最多只能连续出现3次,才能被正则表达式匹配到,但是正如上图所示,没有使用--posix选项或者--re-interval选项时,awk无法根据正则表达式对文本进行处理,因为上例的正则中包含类似"{x,y}"这样的次数匹配字符,所以,在使用正则模式时,如果对应的正则表达式中包含类似"{x,y}"这样的次数匹配字符,则需要使用--posix选项或者--re-interval选项。

行范围模式

其实,只要理解了正则模式,再理解行范围模式,就容易多了。

在介绍行范围模式之前,先来思考一个小问题,有一个文本文件,文件内容如下。

[root@server01 ~]# cat test4
 Allen Phillips
 Green Lee
 William Aiden James Lee
 Angel Jack
 Tyler Kevin
 Lucas Thomas
 Kevin
[root@server01 ~]# awk '/Lee/,/Kevin/{print $0}' test4
 Green Lee
 William Aiden James Lee
 Angel Jack
 Tyler Kevin

上例中的行范围模式的语法是什么意思。

我们可以把上述行范围模式的语法与正则模式的语法对比着理解,可能更加方便我们理解。

 

 

 上图中第一种语法是正则模式的语法,表示被正则表达式匹配到的行,将会执行对应的动作。

上图中第二种语法是行范围模式的语法,它表示,从被正则1匹配到的行开始,到被正则2匹配到的行结束,之间的所有行都会执行对应的动作,所以,这种模式被称为行范围模式,因为它对应的是一个范围以内的所有行,但是需要注意的是,在行范围模式中,不管是正则1,还是正则2,都以第一次匹配到的行为准,就像上述示例中,即使Lee在第2行与第3行中都出现了,但是由于正则1先匹配到第2行中的lee,所以,最终打印出的内容从第2行开始,即使Kevin在第5行与第7行中都出现了,但是由于Kevin第一次出现在第5行,所以最终打印出的内容到第5行结束,也就是说,最终打印出了第2行到第5行以内的所有行。

 

但是,你可能会有这样的需求,你不想依靠正则表达式去匹配行的特征,你只是想单纯的打印出从X行到Y行之间的所有行。

比如,我们有一个文本文件,这个文件中一共有7行文本,你想要打印出从第3行到第6行之间的所有行,该怎么做呢?

其实,使用之前学习到的"关系运算符模式",即可满足我们的需求,示例如下。

[root@server01 ~]# awk 'NR>=3 && NR<6 {print $0}' test4
 William Aiden James Lee
 Angel Jack
 Tyler Kevin

其他

其实,在学习"关系表达式模式"时,有一个"关系运算符"需要与"正则模式"配合使用,它就是"~"

还记得我们之前总结的一些常用的关系运算符吗,我们来回顾一下。

关系运算符

含义

用法示例

<

小于

x < y

<=

小于等于

x <= y

==

等于

x == y

!=

不等于

x != y

>=

大于等于

x >= y

>

大于

x > y

~

与对应的正则匹配则为真

x ~ /正则/

!~

与对应的正则不匹配则为真

x !~ /正则/

没错,细心如你一定发现了,关系运算符"~"与关系运算符"!~"都需要配合"正则模式"使用。

我们来看一个小示例,就更容易理解了。

比如,我想要从如下文本中找出,网卡1的IP地址在192.168.0.0/16网段内的主机,该怎么办呢?

[root@server01 ~]# cat test5
主机名 网卡1的IP        网卡2的IP
主机A  192.168.1.123    192.168.1.124
主机B  192.168.2.222    172.16.100.2
主机C  10.1.0.1         172.16.100.3
主机D  10.1.2.1         192.168.1.60
主机E  10.1.5.1         172.16.100.5
主机F  192.168.1.234    172.16.100.6
主机G  10.1.7.1         172.16.100.7

我们可以使用如下命令,利用关系运算符与正则模式,达到我们的目的。

[root@server01 ~]# awk --posix '$2~/192\.168\.[0-9]{1,3}\.[0-9]{1,3}/{print $1,$2}' test5
主机A 192.168.1.123
主机B 192.168.2.222
主机F 192.168.1.234

上述示例中,$2为awk的内置变量,表示文本中的第2列,"$2~/正则/"表示文本中的第2列如果与正则匹配,则执行对应的动作,对应的动作为"{print $1,$2}",表示打印文本中的第1列与第2列,上例中的正则表达式我就不再赘述了,如果你还不太了解正则表达式,可以参考博客中的正则系列文章。

awk的模式,模式可以总结为如下5种。

1、空模式

2、关系运算模式

3、正则模式

4、行范围模式

5、BEGIN/END模式

 

posted @ 2020-12-28 10:44  星火撩原  阅读(98)  评论(0编辑  收藏  举报