shell 正则表达式

 

知识内容:
* 定义正则表达式
* 基础知识
* 扩展模式
* 创建表达式
在shell脚本中成功地使用sed编辑器和gawk程序的关键是自如地使用正则表达式,设法从大批数据中筛选特定的数据非常复杂。所以说,这是一个难点!
1、正则表达式
1.1、正则表达式的定义
linux使用程序在输入数据时,将正则表达式模式和数据进行匹配,如果数据与模式一致就接受处理。如果数据与模式不一致,它就拒绝处理。像*通配符在匹配linux程序就是最简单的正则表达式应用了。
1.2、正则表达式的类型
在linux领域,几个不同的应用程序要使用不同类型的正则表达式。这些不同的应用程序包括编程语言(Java、Perl以及Python)、linux实用程序(如sed编辑器、gawk程序、grep实用程序),以及主流应用程序(如MYSQL和PostgreSQL数据库服务器程序)
正则表达式可以使用正则表达式引擎实现,正则表达式引擎是解释正则表达式并使用这些模式匹配文本的基础软件。在linux领域,常用的正则表达式引擎有两种:
* POSIX基本正则表达式(BRE)引擎
* POSIX扩展正则表达式(ERE)引擎
大多数的linux使用程序至少要符合POSIX BRE引擎规范,能够识别其定义的所有模式符号。
下面就讲述最常见的正则表达式,并说明如何在sed编辑器和gawk程序中使用这些正则表达式。

2、定义BRE模式
最基本的BRE模式是匹配数据流中的文本字符。
2.1、纯文本
先回顾一下sed和gawk下文本匹配的模式:
[root@wzp ~]# echo "welcome to 51cto" | sed -n '/51cto/p' 
//-n表示把匹配的文本输出到STDOUT,其他的任何文本都不输出到STDOUT
welcome to 51cto
[root@wzp ~]# echo "welcome to 51cto" | gawk '/51cto/{print $0}'
welcome to 51cto
正则表达式关键是匹配与数据流中的文本,而不在乎文本出现在数据流中的位置,出现的次数也无关紧要。
对于大小写的匹配也是很严格:
[root@wzp ~]# echo "Welcome to CHINA" | sed -n '/welcome/p'
[root@wzp ~]# echo "Welcome to CHINA" | sed -n '/Welcome/p'
Welcome to CHINA
还有就是不必局限于文本的完整性问题,所定义的文本出现在数据流的任意位置都可以匹配:
[root@wzp ~]# echo "the book is expensive" | sed -n '/books/p'
[root@wzp ~]# echo "the books are expensive" | sed -n '/book/p'
the books are expensive
尽管数据流中的文本是books,但数据流中的数据包含正则表达式book就可以打印出来;反之,定义的正则表达式books(文本中只有book,没有books)并没有能够匹配的数据流,因此匹配失败而无法打印。
还有就是先前已经演示过的,在正则表达式的匹配中不必局限单个单词,也可以在文本字符串中包含空格和数据:
[root@wzp ~]# echo "this is number 1" | sed -n '/ber 1/p'
this is number 1
如上在正则表达式中已经定义了一个空格,它就必须出现在数据流中
2.2、特殊字符
正则表达式认可的特殊字符有:
. * [ ] ^ $ { } \ + ? | ( )
如果要在文本模式中使用他们就必须使用反斜杠做一个转义(\),例如要在文本中匹配美元符号:
[root@wzp ~]# cat file
this costs $24.0
[root@wzp ~]# sed -n '/\$/p' file
this costs $24.0
对于反斜杠的使用也必须转义才能使得正则表达式匹配数据流:
[root@wzp ~]# echo "\ is a special character" | sed -n '/\\/p'
\ is a special character
最后还有点注意的是虽然正斜杠(/)不是正则表达式的特殊字符,但如果在sed编辑器或gawk程序的正则表达式模式中使用也需要转义的:
[root@wzp ~]# echo "/ is also a special character" | sed -n '///p'
sed: -e expression #1, char 3: unknown command: `/'
[root@wzp ~]# echo "/ is also a special character" | sed -n '/\//p'
/ is also a special character
2.3、定位符
上面讲述的在默认情况下,在指定正则表达式模式时,只要模式出现在数据流之中就可以匹配。
这里头的两个特殊字符可以用来将模式定位到数据流行的开头或结尾。
2.3.1、从头符始
开脱字(^)定义从数据流中文本行开头开始的模式,如果该模式位于文本行的其他任意位置则无法匹配了!
[root@wzp ~]# echo "the book is great" |sed -n '/^book/p'
[root@wzp ~]# echo "the book is great" |sed -n '/^the/p'
the book is great
脱字符定义了文本行开头必须是the(由换行符确定),否则定义的正则表达式就没有能够匹配的数据流了。
[root@wzp ~]# cat file2
That is great
THAT is great
that is great
yeah, that is great
[root@wzp ~]# sed -n '/^that/p' file2
that is great
如上同样的理解,依旧是匹配that开头的文本行。
但这里有点需要注意的是,如果脱字符放在模式的其他位置,它就充当普通字符而不再是特殊字符了:
[root@wzp ~]# echo "this ^ is a test" | sed -n '/^/p'
this ^ is a test
把文本中含有脱字符的打印出来,则出现了正则表达式得以匹配的数据流而输出到STDOUT。
2.3.2、查找结尾
跟脱字符类似的,如果要找跟行尾模式匹配的文本,通过美元符号($)即可定位:
[root@wzp ~]# echo "welcome to IT website 51cto.com" | sed -n '/.com.cn$/p'
[root@wzp ~]# echo "welcome to IT website 51cto.com" | sed -n '/.com$/p'
welcome to IT website 51cto.com
从上面结果就得知最后以.com结尾的文本能够使得正则表达式匹配到数据流而echo出来。
2.3.3、联合定位
联合定位表示说同时使用脱字符和美元符号来实现数据流的匹配,最典型的就是去除文本中的空行:
[root@wzp ~]# cat file3
this is a test line

that is empty above
thai is empty too below

test is over !
现在可以看到上面的文本出现两行空行,这里就可以采用d来把这两行delete掉,如下:
[root@wzp ~]# sed  '/^$/d' file3
this is a test line
that is empty above
thai is empty too below
test is over !
如上所定义的正则表达式模式只查找行头和行尾之间没有内容的行,然后delete掉,剩下的内容就输出到STDOUT,注意这里就不要使用-n选项了,不然输出则为空了。
所以这是一个非常好的方法提炼出某下服务配置文件的主题内容,比如说提取出Nginx的主配置文件初始内容,我们知道该配置文件存在很多#注释行,我们也需要把这些行去掉:
[root@wzp ~]# cat /usr/local/nginx/conf/nginx.conf | grep -v "#" | sed '/^$/d'
worker_processes  4;
events {
    worker_connections  1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    access_log  logs/access.log main buffer=1k;
    sendfile        on;
    keepalive_timeout  65;
    gzip  on;
........省略掉........
由此可见联合定位是一种很不错的方法。记得别使用-n选项,否则显示为空!
2.3.4、点字符
点字符用于匹配除换行符之外的任何单个字符,但点字符必须匹配一个字符。先看个例子:
[root@wzp ~]# cat file4
at home
this is a test line
the cat is sleeping
that is a nice hat
the game is over at ten o'clock
[root@wzp ~]# sed -n '/.at/p' file4
the cat is sleeping
that is a nice hat
the game is over at ten o'clock
这里应该能理解at home为何无法匹配,因为前面没有任何字符,而其他行中都包含at这个文本字符串,并且at前面还存在至少一个字符(哪怕为空格也被视为一个字符),所以才得以显示。
2.3.5、字符类
点字符适合匹配某一字符位置上的任意字符,但如果要限制匹配的字符则需要使用字符类。
首先来看一个例子在说说它的概念吧,同样采用上面的file4:
[root@wzp ~]# cat file4
at home
this is a test line
the cat is sleeping
that is a nice hat
the game is over at ten o'clock
[root@wzp ~]# sed -n '/[ch]at/p' file4
the cat is sleeping
that is a nice hat
这里头的[ch]at表示匹配模式为cat和hat,只有文本中出现这两种类型的文本之一就得以匹配。
回头来说说字符类的概念,它是通过使用方括号,然后把定义的字符类视为一个整体,跟其他通配符一样。
如上的例子跟点字符不同在于仅仅出现at(哪怕前面至少有一个字符)也是不匹配的,必须前面出现c或者h才行。所以这点实际上也不难理解,习惯性使用它就OK了。再来看看一个例子,都很好理解:
[root@wzp ~]# echo "Yes" | sed -n '/[Yy]es/p'
Yes
[root@wzp ~]# echo "yes" | sed -n '/[Yy]es/p'
yes
通过这种匹配模式,我们就可以来判断一下手机号码前三位了:
[root@wzp ~]# cat file5
135
150
1580
16
188
[root@wzp ~]# sed -n '/[1][0123456789][0123456789]/p' file5
135
150
1580
188
从结果来说,对于只有两个字符的行匹配不上这是预料之中的,不过1580也匹配上了其实也不矛盾。
因为只要匹配字符类的要求就行了,而不要求后面的内容。所以要仅仅匹配3个字符,可以通过联合定位的方法来解决这个问题:
[root@wzp ~]# sed -n '/^[1][0123456789][0123456789]$/p' file5
135
150
188
在[]的左右添加^$使得文本匹配只有三个字符,则得到了我们想要的结果。
2.3.6、否定字符类
在正则表达式模式中,可以颠倒字符类的作用,查找不在该字符类中的字符,这里只需要在字符类范围的开头添加脱字符(不是感叹号!),看一个例子:
[root@wzp ~]# cat file4
at home
this is a test line
the cat is sleeping
that is a nice hat
the game is over at ten o'clock
[root@wzp ~]# sed -n '/[^h]at/p' file4
the cat is sleeping
the game is over at ten o'clock
通过否定字符,正则表达式模式可以匹配除了hat文本模式之外的任何字符,包括空格字符,但at前面必须之手一个字符,我们可以验证一下:
[root@wzp ~]# cat file4
at home
at home
this is a test line
the cat is sleeping
that is a nice hat
the game is over at ten o'clock
[root@wzp ~]# sed -n '/[^h]at/p' file4
at home
the cat is sleeping
the game is over at ten o'clock
第二行的at home前面有一个空格,则通过sed匹配后显示出了。实际上最后一行也算是一个例子了!
2.3.7、使用范围
先前举了一个匹配手机号码前三位的例子,使用了从0到9的数据段显得很冗长,实际上这里头可以通过使用短划线符号在字符类中使用一系列字符范围,还是举这么个例子:
[root@wzp ~]# sed -n '/^[1][0-9][0-9]$/p' file5
135
150
188
直接通过0-9来替代一连串的数据。同样的方法也适合于字母a-z,看个例子:
[root@wzp ~]# cat file4
at home
at home
this is a test line
the cat is sleeping
that is a nice hat
the game is over at ten o'clock
[root@wzp ~]# sed -n '/[a-gk-z]at/p' file4
the cat is sleeping
对于模式[a-gk-z]表示从a到g和从k到z的字母范围里头一个字母附带at这样的字符串出现在文本中,则正则表达式能够匹配文本数据流而显示。
[root@wzp ~]# sed -n '/[a-dg-z]at/p' file4
the cat is sleeping
that is a nice hat
2.3.8、特殊字符类
除了定义自己的字符类之外,BRE还包含特殊字符类,可以用于匹配特定类型的字符,如下表:
***********************BRE特殊字符类*******************************
    类                描述
[[:alpha:]]          匹配任意字母字符、大写或者小写
[[:alnum:]]          匹配任意字母数字字符,0~9,A~Z,a~z
[[:blank:]]          匹配空格或者制表符字符
[[:digit:]]          匹配0~9之间的数字
[[:lower:]]          匹配任意小写字母字符,即a~z
[[:print:]]          匹配任意可打印字符
[[:punct:]]          匹配标点符号
[[:space:]]          匹配任意空白字符;空格、制表符、NL/FF/VT/CR
[[:upper:]]          匹配任意大写字母字符,即A~Z
*******************************************************************
在正则表达式中,完全可以像使用普通字符类一样使用特殊字符类:
[root@wzp ~]# echo "abc" |sed -n '/[[:digit:]]/p'
[root@wzp ~]# echo "abc" |sed -n '/[[:alpha:]]/p'
abc
[root@wzp ~]# echo "abc123" |sed -n '/[[:digit:]]/p'
abc123
通过对应BRE特殊字符表就可以理解数据的匹配模式了。
2.3.9、星号
在某个字符之后加一个星号(*)表示该字符必须在匹配模式的文本中不出现或出现多次:
[root@wzp ~]# echo "bk" | sed -n '/bo*k/p'
bk
[root@wzp ~]# echo "bok" | sed -n '/bo*k/p'
bok
[root@wzp ~]# echo "book" | sed -n '/bo*k/p'
book
[root@wzp ~]# echo "boooook" | sed -n '/bo*k/p'
boooook
从它的概念上九很好理解,不出现字符o,那么bk依旧匹配文本数据流,出现字符o一次或者多次都匹配文本数据流。这里还可以通过星号和使用范围来实现更加广泛的匹配模式:
[root@wzp ~]# echo "bt" | sed -n '/b[ea]*t/p'
bt
[root@wzp ~]# echo "bet" | sed -n '/b[ea]*t/p'
bet
[root@wzp ~]# echo "bat" | sed -n '/b[ea]*t/p'
bat
[root@wzp ~]# echo "beat" | sed -n '/b[ea]*t/p'
beat
[root@wzp ~]# echo "beaat" | sed -n '/b[ea]*t/p'
beaat
[root@wzp ~]# echo "bct" | sed -n '/b[ea]*t/p'
[root@wzp ~]# echo "bect" | sed -n '/b[ea]*t/p'
[root@wzp ~]# echo "baeeat" | sed -n '/b[ea]*t/p'
baeeat
只有e和a字符的组合出现在b和t之间(那怕完全不出现也匹配),模式就匹配。但如果出现其他字符,则匹配失败。

2.4、扩展的正则表达式
POSIX ERE模式包括某些linux应用出现和工作使用的其他几个符号。gawk程序能够识别ERE模式,sed编辑器则不能!有点需要注意的是:sed编辑器和 gawk程序的正则表达式引擎存在差异。gawk程序可以使用大部分扩展正则表达式模式的符号,并且具有sed编辑器没有的其他一些筛选能力。但是,也正是由于这点,它处理数据流的速度通常较慢。
2.4.1、问号
问号和星号相似,但不同的是问号(?)表示其前面的字符可以不出现或者出现一次,否则不匹配:
[root@wzp ~]# echo "bk" | gawk '/bo?k/{print $0}'
bk
[root@wzp ~]# echo "bok" | gawk '/bo?k/{print $0}'
bok
[root@wzp ~]# echo "book" | gawk '/bo?k/{print $0}'
如上显示说明字符o要么不出现、要么仅能出现一次,正则表达式才能匹配文本数据流。
正如星号一样,问号也可以使用范围连用:
[root@wzp ~]# echo "bok" | gawk '/b[ao]?k/{print $0}'
bok
[root@wzp ~]# echo "bak" | gawk '/b[ao]?k/{print $0}'
bak
[root@wzp ~]# echo "bk" | gawk '/b[ao]?k/{print $0}'
bk
[root@wzp ~]# echo "baok" | gawk '/b[ao]?k/{print $0}'
要知道,同时出现ao是无法使得匹配文本数据流的,因为问号(?)前面的字符最多出现一次,否则不匹配
2.4.2、加号
加号与星号相似,表示其前面的字符可以出现一次或多次(至少出现一次),则匹配模式:
[root@wzp ~]# echo "bk" | gawk '/bo+k/{print $0}'
[root@wzp ~]# echo "bok" | gawk '/bo+k/{print $0}'
bok
[root@wzp ~]# echo "boook" | gawk '/bo+k/{print $0}'
boook
对于字符类的匹配,作用方式跟星号和问号一样:
[root@wzp ~]# echo "boook" | gawk '/b[oa]+k/{print $0}'
boook
[root@wzp ~]# echo "boaok" | gawk '/b[oa]+k/{print $0}'
boaok
[root@wzp ~]# echo "bk" | gawk '/b[oa]+k/{print $0}'
[root@wzp ~]# echo "bak" | gawk '/b[oa]+k/{print $0}'
bak
如上的显示结果非常好理解,不用多说了。
2.4.3、使用大括号
在ERE中,可以使用大括号对可重复的正则表达式作限制,这通常叫做间隔,可以用两种格式表示间隔:
* m: 该正则表达式正好出现m次
* m,n: 该正则表达式出现最少m次,最多n次
注意点:默认情况下,gawk程序不能识别正则表达式间隔,必须指定--re-interval命令行选项,以便gawk程序识别正则表达式间隔。
[root@wzp ~]# echo "bk" | gawk --re-interval '/bo{1}k/{print $0}'
[root@wzp ~]# echo "book" | gawk --re-interval '/bo{1}k/{print $0}'
[root@wzp ~]# echo "bok" | gawk --re-interval '/bo{1}k/{print $0}'
bok
通过指定间隔为1,则必须仅仅出现bok才能得以匹配
对于指定上下限间隔的例子:
[root@wzp ~]# echo "booook" | gawk --re-interval '/bo{1,3}k/{print $0}'
[root@wzp ~]# echo "boook" | gawk --re-interval '/bo{1,3}k/{print $0}'
boook
[root@wzp ~]# echo "bok" | gawk --re-interval '/bo{1,3}k/{print $0}'
bok
凡是出现1~3个o的模式则匹配成功。当然还可以适用于字符类:
[root@wzp ~]# echo "bk" | gawk --re-interval '/b[oa]{1,3}k/{print $0}'
[root@wzp ~]# echo "bok" | gawk --re-interval '/b[oa]{1,3}k/{print $0}'
bok
[root@wzp ~]# echo "boaok" | gawk --re-interval '/b[oa]{1,3}k/{print $0}'
boaok
[root@wzp ~]# echo "boaoak" | gawk --re-interval '/b[oa]{1,3}k/{print $0}'
[root@wzp ~]# echo "baaak" | gawk --re-interval '/b[oa]{1,3}k/{print $0}'
baaak
对于显示结果应该不用怎么解释了,跟前面的完全一样。
2.4.4、管道符号
管道符号允许我们用逻辑or公式指定正则表达式检查数据流时使用的两个或多个模式,如果任何一个模式与数据流文本匹配,则文本通过。如果没有一个匹配的,数据流文本匹配失败。看个例子:
[root@wzp ~]# echo "the pig is sleeping" | gawk '/cat|dog/{print $0}'
[root@wzp ~]# echo "the dog is sleeping" | gawk '/cat|dog/{print $0}'
the dog is sleeping
[root@wzp ~]# echo "the cat is sleeping" | gawk '/cat|dog/{print $0}'
the cat is sleeping
这个应该很好理解,管道符号两边的任何正则表达式可以使用任意正则表达式模式(包括字符类)来定义文本
[root@wzp ~]# echo "the cat is sleeping" | gawk '/[ch]at|dog/{print $0}'
the cat is sleeping
[root@wzp ~]# echo "where is my hat?" | gawk '/[ch]at|dog/{print $0}'
where is my hat?
2.4.5、将表达式分组
正则表达式模式也可以使用圆括号分组,一个组合作为 一个标准字符处理,如下看一个将分组和管道组合使用以创建可能的模式匹配组:
[root@wzp ~]# echo "gac" | gawk '/(c|d)a(t|g)/{print $0}'
[root@wzp ~]# echo "dag" | gawk '/(c|d)a(t|g)/{print $0}'
dag
[root@wzp ~]# echo "cat" | gawk '/(c|d)a(t|g)/{print $0}'
cat
实际上感觉这里头的括号作用跟中括号很接近。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值