之前有写过两篇关于sedawk的文章,但是内容太过简单了。近期打算就这方面话题写一个系列,系统介绍一些与shell script有关的内容。
因为即将介绍的一系列内容都将与正则表达式有关联,所以作为这个系列的开篇之作,先详细介绍一下正则表达式的使用。关于正则表达式的文章相信已经数不胜数了,我之所以再写一篇,一方面结合自己学习的心得用自己的话来组织一下,希望对刚接触正则的朋友有所帮助;另一方面也是自己再做个总结写个笔记加深理解。
进入正题吧。首先要解决的一系列问题是:正则是什么,哪些场合用到正则,正则能解决什么问题?(注:我下面要给出的一系列定义都未必是各大书籍的经典定义,而是我自己理解自己组织的语言)
正则表达式描述了特殊的字符序列,也就是,正则表达式的结果就是一系列字符,不论它有多复杂或是多简单。我们接下来要讨论的正则表达式(或称Regular Expression,简称RE)都是指linux/unix系统中grepedsed工具中所使用的RE(使用RE的并非只有这些工具,例如phpperl都使用RE,但是他们不在我们讨论的范围之内)。为什么不提egrepawk呢?因为他们使用的REgrepsed所使用的有些很小的区别,他们将在本文的后半部分提到。
还是举点例子吧,晦涩的文字太不直观了!我们下面的大多数例子都将以grep工具来讲,因为greplinux/unix系统最常用最简单而且要使用RE的工具!既然要拿grep来举例,还是先介绍一下grep吧。grep命令的作用是在一个或多个文件中搜索特定的字符串,语法为:
grep pattern file(s)
每个文件中符合模式pattern的字符串所在的行都将显示在终端上。(注意,这里的pattern将广泛使用在grepsedawk中,事实上RE就是使用在pattern中的!)最简单的一个例子:
grep ‘root’ /etc/passwd
上面这个grep命令中,root就是一个最简单的RE!整条命令的意思就是:在文件/etc/passwd中搜索包含字符串root的行,并把它(们)显示出来。(这里的单引号’’要讨论起来过于复杂,我们将在其他的文章中重点讨论,记得先这么用吧grep ‘xxxx’ file
为什么root也是一个RE呢?别把RE看得过于复杂,在不使用特殊字符(在RE中称为元字符)的情况下,RE就是一个一个的字符而已!记住: RE都只是处理单个字符的!root这个pattern到底是怎么工作的呢?很简单,就是在每一行先找r字符,如果找到了,再依次找oot。换句话说,就是要找到r后面紧跟着o再紧跟着o再紧跟着t的,才把该行显示出来。再复杂的RE也都是这样工作的!那RE岂不是很简单?它难在哪里呢?难就难在它最强大的地方,模糊匹配,也就是下面要讲的元字符。(如果没有元字符,都像’root’这样,也就没必要搞什么RE了,pattern里写个单词不就得了)
重点难点的元字符来了啊!很多书和文章都会一鼓脑儿把元字符列一张表,让学习者去理解或者背下来。这样的效果是,那张表不结合实例看三遍以上是理解不了的!结合我以前学习的心得,我把元字符分成2类来介绍。其实元字符总共也没几个,分类是为了便于理解。
第一类:可以单独使用的元字符。这类字符有(下面列出来的字符都请一个一个的看,不要连起来,因为他们都是独立存在就有意义的)
.  ^  $
.代表任意单个字符
^作为RE的第一个字符,代表行的开始;在其他位置是普通字符
$作为RE的最后一个字符,代表行的结束;在其他位置是普通字符
举例:
 
 
$cat file
11111.1
2222^2
3333$3
                  注意这是一个空行
$grep ‘.’ file
11111.1
2222^2
3333$3
为什么会显示三行呢?因为命令中的.是元字符,而不是普通的点。元字符.代表任何单个字符,所以只要有字符的行,这条grep命令都会把它显示出来。
$grep ‘^’ file
11111.1
2222^2
3333$3
                  注意这里的空行也显示了
为什么没有^符号的行也显示呢?因为这个RE^是在第一个字符位置,这里的^代表行首,只要有行首的行都显示(任何行哪怕是空行都有行首和行尾)同样的道理grep ‘$’ file也会显示所有行。
$grep ‘.^’ file
2222^2
这个命令中的^前面有个元字符,所以这里的^不表示行首,而是个普通字符;.^表示一个任意字符后面紧跟字符^,这里匹配的内容就是2^,于是这一行被显示。
$grep ‘$.’ file
3333$3
这个命令中的$后面有一个元字符,所以不表示行尾,$.匹配的是$3,于是该行被显示。
$grep ‘^$’ file
                  这里显示了一个空行
^$匹配行首和行尾之间没有字符的行,也即是空行了。^$表示空行,而^ $(中间有个空格哦)表示只有一个空格的行。
好了,第一类元字符介绍完了,简单吧,只有三个而已。
第二类,用来修饰其他字符(普通字符或第一类元字符)的元字符,这一类元字符要多一些,一个一个来吧。
常用的匹配单个字符的还有[][…]匹配括号中的字符之一。下面列举一些常见的使用方式:
[abc]      匹配单个字符abc
[123]          匹配单个字符123
[a-z]           匹配小写字母a-z之一
[a-zA-Z]     匹配任意英文字母之一
[0-9a-zA-Z]匹配任意英文字母或数字之一
注意上面标红色的单个和之一了吧,不管[]里面多复杂,它的结果都是一个字符!
[]里面的字符值得注意的有几个特例:
-   ^  [  ]       (也请分开一个一个看)
-在上面的例子已经提到了,表示范围。但是注意:如果-符号出现在[]里面的首尾位置是普通字符。例如:
[-abc-]        匹配字符-abc之一
-符号虽然出现了2次,这里和一个-是同样的意义,试想aa之一和aaa之一有区别吗?
^符号如果出现在[]的起始位置表示否定,但是在其他位置是普通字符
[^ab^c] 匹配不是ab^c的任意字符
$cat file
Aaaaabc
Bbbbbbc^
cccccbc33
$grep ‘[^ab^c]’ file
cccccbc33
只有第三行的3满足了匹配要求,所以只显示这一行。
$grep ‘[x^]’ file
Bbbbbbc^
[x^]匹配字符x^
如果[]中要包含字符[]该怎么办呢,[][]应该怎么理解呢?[[]]又怎么理解呢?有一个原则:字符][放在[]中,只有紧跟在[后面的]和紧贴于]前面的[是普通字符。还有一个更好理解的方式,就是用反斜杠转义[]中的[],如[\[\]],当然这看起来同样不那么舒服。
除了[]之外,还有很多第二类元字符,先来整体认识一下他们吧(这次不是一个一个看了,以空格为分界来分段看吧)
*   \    \?      \+    \{n,m\}
* 用于修饰前导字符,表示前导字符出现任意多次
\? 用于修饰前导字符,表示前导字符出现01
\+ 用于修饰前导字符,表示前导字符出现1或多次
\{n,m\}  用于修饰前导字符,表示前导字符出现nm nm都是整数,且n<m
\  用于转义紧跟其后的单个特殊字符,使该特殊字符成为普通字符
注:以上“前导字符”表示紧贴于元字符前面的单个普通字符或第一类元字符。举例吧:
a* 匹配连续的任意(也包括0)个a
.* 匹配连续的任意(也包括0)个任意字符,传说中的万能匹配!
a\? 匹配01a
a\+ 匹配1或多个a
a\{3,5\} 匹配35个连续的a
\.* 匹配0或多个连续的.  \.表示普通字符句点.
注意到没有,*  \?  \+  \{n,m\}这几种元字符完成类似的功能,都是匹配连续出现的前导字符,只是出现的次数不一样罢了,不是很难吧。
\{n,m\}还有其他几种形式:
\{n\}  连续的n个前导字符
\{n,\}  连续的至少n个前导字符
发现没有,\?等价于\{0,1\}  *等价于\{0,\}   \+等价于\{1,\}
特别提一下,你可能会在很多地方看到?+,而不是这里说的\?\+。这个问题我在开篇有所提及。grep/sed用的是\?\+,普通正则REegrep/awk用的是?+,扩展正则ERE。仅这点区别而已,REERE大部分是相同的。
还有一组元字符也是常用的,但是相对比较难理解,拿到这里单独说一下:
\(  \)
这里的\(\)一定是成对出现的,这对带反斜杠的小括号的意义是:将小括号中匹配的字符串存储到下一个寄存器中(1-9)\( \)seds///替换中经常使用,其实它并不限于应用在sed中,举例:
^\(.\)  行中第一个字符存到1号寄存器中(为什么是行中第一个字符?回上面复习一下吧
^\(.\)\1行首两个字符,且他们相同
^\(.\).*\1$ 行首尾两个字符相同
\1的意思就是取1号寄存器的内容,这是固定格式\n1n9
$cat file
aaaaabc
bc^b
1234
gdfdd
$ grep '^\(.\)\1' file
aaaabc
$ grep '^\(.\).*\1$' file
bc^b
正则表达式理论方面的就介绍到这里了,下面再补充一些更常用也更长一点的RE例子。
[0-9a-z?,.;:’”]
这个表达式将匹配“任意单个字符,可以是数字、小写字母、问好、逗号、句号、分号、冒号、单引号或双引号
[0-9]\{2\} 连续的两位数字
[0-1][0-9][-/][0-3][0-9][-/][0-9]\{2\}
这个表达式能够用来表示时间格式MM-DD-YYMM/DD/YY,方括号[]中的-放在第一个位置,确保它在[]中不被解释为范围,而是普通字符-
还有一点要注意的,就是\{2\}仅是用来修饰前面的单个字符的,别忘了[0-9]也是单个字符
gep ‘<.*>’ file  将匹配所有带有<>标记的行
can[ no’]*t 将匹配cantcan tcanntcannotcann’tcannnnnnt等等。。。
80[234]\ ?86将匹配8086802868038680486
^$ 匹配空行
^.*$.*都匹配任意行(包括空行)
[0-9][0-9]*\.\{5,\}[0-9][0-9]*
匹配“至少一位数字紧跟至少5个句点紧跟至少一位数字。这个正则应该分段来看:
[0-9]是一位数字;[0-9]*0或多位数字;[0-9][0-9]*就是“至少一位数字”,相当于[0-9]\+
\.\{5,\}中的.被转义了,表示普通的.  于是\.\{5,\}就表示至少5个句点
先举这些例子吧,更多的例子以后再更新了