正则表达式与Linux三剑客:

引言:

在Linux中,支持通配符(*,?)的命令有很多,但是支持正则表达式的命令只有Linux三剑客(sed,awk,grep),正则表达式是处理大量字符串强有力的一个工具,学好它吧,加油!!

一.正则表达式:

补充:

(1)/<,/>:这两个符号表示匹配内容要有前后边界,没有前后边界的内容则不会被匹配,一般使用正则的时候都要加上这个符号,否则很可能产生意想不到的匹配结果

(2)匹配空格:[[:space:]]

举例:

[root@Guistar ~]# cat li1
I----love----IU
IloveIU
Uaena,love:IU
Guistarlove?IU{
[root@Guistar ~]# grep "\<love\>" li1
I----love----IU
Uaena,love:IU
[root@Guistar ~]# grep "love\>" li1
I----love----IU
Uaena,love:IU
Guistarlove?IU{
[root@Guistar ~]# 

这个符号可能是最重要的两个了,它们明确规定了单词的边界问题

1.限定符:

(1)^:用于模式的最左侧,如:^boy:用于匹配以boy这个单词开头的行

(2)$:用于模式的最右侧,如:^boy:用于匹配以boy这个单词结尾的行

(3)^$:组合符,用于匹配空行

(4).:匹配任意一个字符,不包括换行符

(5)*:前面的字符可以没有,也可以重复多次,注意这里的重复并不是一摸一样,而是只要符合前面的正则表达式规定即可

如果本行没有*前面指定的字符,则匹配一整行,可见,每一行都会多多少少被*匹配一点东西(俗称:雨露均沾)

(6).*:包罗万象,什么都能匹配,包括空行

(7)\:转义字符,如\.的意思是将.转义为最字符小数点

(10)[abc]:匹配中括号内任意一个字符,也可以写为[a-c]

(11)?:匹配前面的字符0次或1次(即前边的字符可有可无)

(12)+:前面的字符匹配1次以上

(13){ }:规定前面的字符的出现次数{2,}:前面的字符出现两次及以上;

​ {2,6}:前面的字符出现2到6次

​ {,6}:前面的字符出现6次以下

(5)():和前边介绍过的几个限定符配合使用(ab)+:表示匹配ababab这样出现一次以上的ab,除此之外,\n还可以引用前边的括号中匹配到的原字符串

2.或运算:

a (cat|dog):匹配"a cat" 或 “a dog”

3.元字符:

\d:数字字符0~9

\w:单词字符,所有的英文字母,数字和下划线

\s:空白字符

\D:非数字字符

\W:非单词字符

\S:非空白字符

\b:表示单词的边界

4.贪婪匹配与惰性匹配:

This is a sample text</b>

以上字符串如果使用<.*>则会匹配一整行,这就是贪婪匹配,

如果使用<.*?>则会匹配到,,</b>,这就是惰性匹配


5.BRE与ERE:

BRE与ERE主要区别体现在限定符上

BRE基本正则表达式:.*[ ]^$

ERE扩展正则表达式:在BRE的基础上加上{}()+?|


7.正则表达式例题:

找出/etc/passwd目录下的shell用户(开头段和结尾段一致)

[root@Guistar ~]# cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
polkitd:x:999:998:User for polkitd:/:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
ntp:x:38:38::/etc/ntp:/sbin/nologin
litubao:x:1000:1000::/home/litubao:/bin/bash
[root@Guistar ~]# grep -E  "^([^:]+\>).*\1$" /etc/passwd
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
[root@Guistar ~]#

三.Linux三剑客:

剑客一 ——grep:查找


grep [选项] [正则表达式] 被查找的文件

选项:(只列出常用的几个)

-E支持扩展的正则表达式
-o只输出匹配的内容
–color=auto指定颜色
-i不区分大小写
-n列出所有的匹配行,显示行号
-v反转匹配模式
-m后面跟数字n,表示匹配n次后,停止匹配,结束命令
-l多文件时,使用这个参数,只会列出存在匹配信息的文件名字

Linux貌似不支持惰性匹配,我试过各种办法,都没有成功的惰性匹配过,不过就支持一些基本的正则表达式就已经很强大了,但是我还是有一点小失落,毕竟。。。这不符合美学,实在像是一个缺陷


剑客二 ——sed:编辑


sed [选项] [sed内置命令符] 文件

sed选项:

-n只显示处理过的行,
-i修改结果到源文件
-e多次编辑,不需要管道符(可以用“;”替代,这样更为简洁)所有高手都不会用-e的**
-r支持正则扩展

特别说明:-n通常和p一起使用,如果不指定-n,并且没有-i选项则处理过和没处理过的行都会被打印,

如果不指定-n但是指定了-i选项,那什么也不会打印


sed内置命令符:

a对指定文本追加,在指定行后里面追加一行或多行文本
d删除匹配行
i插入文本,在指定行前面插入一行或多行文本
p打印指定行的内容,通常和n一起使用
s/被替换的内容/新内容/g然后替换内容(同样也支持正则),结尾g表示全局匹配

sed匹配范围:

范围解释
空地址全文处理
单地址指定文件某一行
/pattern/被模式匹配到的每一行
范围区间10,20:第10~20行;10,+5:第十行向下5行;5,$:第5行及其下面的所有行;/pattern1/,/pattern2/:从
符合pattern1的行到符合pattern2的行
步长1~2表示1,3,5,7…行;2~2表示2,4,6,8,10…行

举例:sed -n ‘2,3p’ re_test:打印出第2,3行

sed -n ‘/基本正则表达式/p’ re_test:打印出符合正则表达式内容所在的行

sed ‘/基本正则表达式/d’ re_test:删除出符合正则表达式内容所在的行

sed ‘5d’ re_test:删除第5行

sed -e ‘s/str1/str2/g’ -e ‘s/str3/str4/g’:将全文的str1替换为str2,同时将str3替换为str4(str不必加双引号)

sed -n ‘1,10s/str1/str2/p’:将1到10行的str1(也可以时正则表达式)替换为str2

sed -e ‘/pattern/s/str1/str2/g’ -e ‘s/str3/str4/g’:将符合pattern的行的str1替换为str2,同时将str3替换为str4

sed ‘2a str’ re_test:在第二行之后追加一行,内容为str(str中可以用\n换行符)

sed ‘2i str’ re_test:在第二行之前插入一行,内容为str(str中可以用\n换行符)


好了,相信现在我的sed剑客已经修炼成仙了,现在来挑战一下高难度的问题

ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.199.100  netmask 255.255.255.0  broadcast 192.168.199.255
        inet6 fe80::f223:f937:5afe:968  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:b6:27:a6  txqueuelen 1000  (Ethernet)
        RX packets 2511  bytes 214728 (209.6 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 1566  bytes 210217 (205.2 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

sed -n ‘2p’ test | sed ‘s/^.*inet//’ | sed ‘s/net.*$’// | sed -n ‘1p’

命令执行结果:192.168.199.100

这个命令有四段:

第一段的作用并不是为了打印第二行,而是先选中第二行,然后通过管道传输给第二段进行下一步处理

第二段的作用是使用正则表达式匹配IP地址前面的所有字符串(但如果你足够细心,你会分析出,有个空格并没有被匹配)

然后删除(即用s命令替换为空)

第三段的作用就不用说了吧

第四段的作用是把结果打印出来

总结:这个例子不但熟悉了s选项的用法 更重要的是极大加强了本人对于管道的理解(-e在某些情况下是无法取代管道的)

*其实还可以用-e:sed -e '2s/^.inet//’ -e ‘2s/net.*$//’ -e ‘2p’ test -n


sed的语法:正常而言,-n,-i,-r等选项可以放两个位置,sed之后或者文件之后,但是如果涉及-e的话,那-e要最先写,然后把

其他的选项写在文件的后面:

正常:sed -n ‘/pattern/p’ 文件 sed ‘/pattern/’ 文件 -n

*有-e:sed -e '2s/^.inet//’ -e ‘2s/net.*$//’ -e ‘2p’ test -n


至此,sed剑客已彻底修炼成仙,使用它去横扫天下吧!


剑客三 ——awk:格式化


1.awk基础:

awk内置变量:

0所有字段
FILENAME当前文件名
RS指定输入时的换行符,默认回车(处理的时候把RS视为一行的结束)
ORS指定输出时的换行符,默认回车(输出的时候在行尾打印ORS)
$n该行第n个字段
NF该行的字段总数
FS输入分隔符
OFS输出分隔符
NR该行的行号
FNR各个文件分别计数的行号
更多变量请咨询手册man awk

awk选项:

-F指定分隔符
-v修改内置变量的时候要加这个选项
-f从脚本中读取awk命令

在这里一定要注意:使用-v的时候,-v只对它后面的一个变量生效,如果要更改多个变量,要写多个-v


(1)语法:awk [options] ‘pattern{action}’ file

a)pattern

​ 作用一:匹配行:

​ **可以写NR==n,用来匹配行,也可以用/正则表达式/来匹配行,**还可以写$n>1000,来匹配第三列大于1000的行

​ 正则表达式取反的方式与grep有所区别,不能再用-v选项了,而是要这样:“!/pattern/”

​ 还可以规定特定的字段来匹配正则,如$x~/pattern/表示字段x匹配正则的行

​ 作用二:BEGIN还可以写BEGIN后面加一个{动作}

BEGIN的作用是在开始处理之前要执行什么动作

​ 作用三: BEGIN知道了,那么END是什么就不必多说了吧

b)action一般是print或者printf,print后面可以跟字符串,变量,用逗号隔开,逗号的意义下文输出分隔符会详细讲

​ printf非常重要,所以在此详细介绍一下。

printf和C语言一样,但是请注意以下几个问题:

printf有两个:Linux命令和awk动作,这里首先介绍Linux命令的printf

第一:printf使用的时候要养成最后加\n的习惯,这一点在Linux中尤其重要

第二:printf格式化字符串和变量之间不能像C语言那样加逗号,而是要用空格隔开两者,如果有多个变量,那么变量之间也

使用空格隔开

第三:字符串变量写的时候可以不加双引号,但是最好是加上,便于理解!

第四:如果不直接写变量的值,而是写变量的引用,那么必须加上$,否则会被Linux视为字符串

第五:printf后面的变量可以写多组,只要每一组保证能和前面的格式化字符串对应上,那就可以成功打印

OK,现在再来看一看awk中的printf

awk中的printf和命令printf基本一样,有两个区别:

第一,不支持命令printf的第五条

第二,printf和格式化字符串之间用空格隔开,格式化字符串和变量,以及变量与变量之间均要像C语言那用逗号隔开

[root@Guistar ~]# cat test
20 财务部 七层C区
30 设计部 七层B区
40 人事部 六层C区
50 采购部 七层A区
60 招聘部 六层A区
70 法务部 七层D区
80 发展部 六层D区
90 总经办 六层E区
[root@Guistar ~]# awk 'BEGIN{print "我喜欢李知恩"} /人事部/{print $0}' test
我喜欢李知恩
40 人事部 六层C区
[root@Guistar ~]#

awk就是用来格式化的,因此熟练掌握printf至关重要,可以说printf就是awk的灵魂,print仅仅是为了和OFS配合,还有结尾不用加\n而已

它只是在某些情况下比printf方便了一点而已,但是它并不支持格式化输出,这就有很大的局限,使用起来不够灵活


(2)打印:

打印出每一行的所有列

awk '{print $0}' file

打印出每一行的第n列(n!=0)

awk '{print $n}' file

打印出每一行的最后一列

awk '{print $NF}' file

打印出每一行的倒数第二列

awk '{print $(NF-1)}' file

一次性打印多列

awk '{print $1,$2}' file

自定义打印:

awk '{print "第一列:",$1,"第二列:",$2,"第三列:",$3}' file

自定义打印的时候,双引号的部分会被识别为字符串


在这里没有使用选项和模式,只是用动作来打印每一行的指定列;由此可见,在默认情况下,awk以空白符作为分隔符

总结:awk按行处理文件,一行处理完毕,处理下一行,根据用户指定的分割符工作,没有指定则默认空格


(4)NR变量:NR变量要区别与NF变量,它使用的时候不用加’$’

#显示第1行到第5行(管道输出也是第1行到第5行):
awk 'NR==1,NR==5' file
#显示第五行到第七行的第二个字段
awk 'NR==5,NR==7{print $2}' file
#打印并显示出每一行的行号
awk '{print NR,$0 }'

(5)awk基础实战:取IP地址

ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.199.100  netmask 255.255.255.0  broadcast 192.168.199.255
        inet6 fe80::f223:f937:5afe:968  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:b6:27:a6  txqueuelen 1000  (Ethernet)
        RX packets 1767  bytes 150883 (147.3 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 1100  bytes 155511 (151.8 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

awk NR==2 ‘{print $2}’

从这个实战中可见awk的强大,用它取IP地址比sed简单很多!


2.awk分隔符:

awk分隔符分为输入分隔符和输出分隔符,输入分隔符是处理文件的时候的分隔符,输出分隔符是输出的时候的分隔符


(1)输入分隔符:

awk逐行处理文本的时候,以输入分隔符为准,把文件分割成多个字段,然后进行相应处理,输入分隔符默认是空格

指定输入分隔符的时候要这样-F “要指定的分隔符”,输入分隔符变量名是FS(前文已提及)

[root@Guistar ~]# cat test3
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
polkitd:x:999:998:User for polkitd:/:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
ntp:x:38:38::/etc/ntp:/sbin/nologin
litubao:x:1000:1000::/home/litubao:/bin/bash
[root@Guistar ~]# awk -F ":" '{print $1}' test3
root
bin
daemon
adm
lp
sync
shutdown
halt
mail
operator
games
ftp
nobody
systemd-network
dbus
polkitd
sshd
postfix
ntp
litubao

以上命令还可以这样写 awk FS=“:” ‘{print $1}’ test3

除此之外,分割符还可以同时指定多个,把多个分隔符写在中括号里面:-F “[ :]”,表示把空格和冒号都作为分隔符


(2)输出分隔符:

awk执行完命令输出屏幕的时候,print中的逗号的意义由输出分隔符决定,

[root@Guistar ~]# cat test3
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
polkitd:x:999:998:User for polkitd:/:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
ntp:x:38:38::/etc/ntp:/sbin/nologin
litubao:x:1000:1000::/home/litubao:/bin/bash
[root@Guistar ~]# awk -v OFS="------" -v  FS=":" '{print $1,$2}' test3
root------x
bin------x
daemon------x
adm------x
lp------x
sync------x
shutdown------x
halt------x
mail------x
operator------x
games------x
ftp------x
nobody------x
systemd-network------x
dbus------x
polkitd------x
sshd------x
postfix------x
ntp------x
litubao------x

同样的,这个命令还可以这样写:[root@Guistar ~]# awk -v OFS=“-------” -F “:” ‘{print $1,$2}’ test3

(3)FS与OFS的内在联系:

awk在进行打印动作的时候是这样工作的,根据FS来把每一行分割为多个列,同时删除分隔符,然后打印print后

面规定的列,打印的时候把双引号外面的逗号视为OFS规定的字符或字符串

(实际上并没有)


3.awk变量:

awk有两种变量:内置变量和自定义变量

(1)awk内置变量:

内置变量至关重要,它是awk格式化的基石,只有熟练掌握各个变量的格式化作用,才能最大限度的发挥awk的作用

需要注意的是,内置变量在使用的时候,通常不加$符号,当然有些(例如NF也可以加)

0所有字段
FILENAME当前文件名
RS指定输入时的换行符,默认回车(处理的时候把RS视为一行的结束)
ORS指定输出时的换行符,默认回车(输出的时候在行尾打印ORS)
$n该行第n个字段
NF该行的字段总数
FS输入分隔符
OFS输出分隔符(可以理解为print后面的逗号或者处理文本时候的分隔符)
NR该行的行号
FNR各个文件分别计数的行号
更多变量请咨询手册man awk

(2)RS和ORS:

在这里特别说明一下两个最为强大的变量:RS和ORS


RS:

在我们一般人的认知中,一行的结束就是回车,而Linux的grep和sed这两位剑客也是这么认为的,他们处理文本以行为单位,一行一行的

处理的,但是awk这位剑客比较有个性,它觉得:“为什么要那么死板呢,非要以行为单位处理呢,我想以什么单位处理就以什么单位处

理”

RS变量的作用就是打破以行为单位处理的限制,你可以使用这个参数任意指定最小处理单位

还可以换一个角度来理解RS这个变量:其实三剑客都是以行为单位进行处理的,但是吧,什么”叫一行“呢?换句话说,一行的结束标志是

什么呢?前两位剑客grep和sed觉得一行理所当然应该以回车结束呀,因此他们在处理文本的时候都是看什么时候到回车,那就标志着

该行的结束 但是把awk这位剑客觉得那样太死板了,他说:“谁规定的一行的结束标志必须就是回车啊,你们这俩货别再误导tubao

了。”awk对着前两位剑客不屑的说到:“一行的结尾到底是什么还得人家tubao说了算啊,我不管,我就听人家tubao的话,人家说什么是

行尾标志,什么就是行尾标志,人家要是不说,那我再用回车!“随后,awk对着操作它的李图报说:”嘿嘿,看我多知道为你着想!“

RS变量的另一种描述:打破行尾标志为回车的定式,由使用者自己决定什么时候到达行尾


ORS:

我们将文本输出到屏幕的时候,每一行打印到行尾的时候在一般的认知里是回车是吧,但是awk比较开方,它特意对李图报说:哎,你说

的算,你说结尾是什么就是什么”

ORS参数的作用是设定一个输出是的行尾标志,在每一行结束的时候打印该标志


(3)RS与ORS的内在联系:

awk处理文本的时候是这样工作的,在处理每一行的时候,如果遇到RS规定的行尾标志,就会删除该标志,然后

记录此时行尾的最后一个字符,然后开始打印这一行,当打印完刚刚标志的字符之后,打印ORS设定的字符


(4)printf格式化输出实例:
[root@Guistar ~]# cat test
20 财务部 七层C区
30 设计部 七层B区
40 人事部 六层C区
50 采购部 七层A区
60 招聘部 六层A区
70 法务部 七层D区
80 发展部 六层D区
90 总经办 六层E区
[root@Guistar ~]# awk 'BEGIN{printf "tubao开始操作了,准备好了吗?\n%-25s\t %-25s\t %-25s\n","数字","部门","区域"} END{print "装逼完成!请大声的夸我吧\n"}  {printf "%-25s\t %-25s\t %-25s\n",$1,$2,$3}' test
tubao开始操作了,准备好了吗?
数字                       	 部门                       	 区域                       
20                       	 财务部                      	 七层C区                     
30                       	 设计部                      	 七层B区                     
40                       	 人事部                      	 六层C区                     
50                       	 采购部                      	 七层A区                     
60                       	 招聘部                      	 六层A区                     
70                       	 法务部                      	 七层D区                     
80                       	 发展部                      	 六层D区                     
90                       	 总经办                      	 六层E区                     
装逼完成!请大声的夸我吧
[root@Guistar ~]#

这是打印表格的经典实例,这里要注意,打印表格的时候,%-25s\t这个\t经过测试是必须要加的,我也不知道什么原因,25的长度既然已经规定好了,那按理说不加的话应该也没问题,哎!记住吧,没办法!


结语:

至此,Linux三剑客已正式称为您的麾下部将,他们将帮助你解决绝大部分处理文本的难题!但是要经常来查看本文档熟悉,你才能熟练的驾驭他们!

更多推荐