在Linux的学习当中,正则表达式是非常重要的部分,现在我想通过grep命令来解析和学习正则表达式,同时也能将grep的相关功能展示清楚。
从一个例子开始
cat test.txt
grep 'a line' test.txt
上述例子我试图从test.txt中获取字符串a line所在的行。
那么这个过程中到底发生了什么呢?我们从下面的示意图中可以有所了解
从上图可以很直观的看出,grep在处理模式匹配的时候,将文件的每一行作为一个单独的字符串,然后和我们设定好的模式逐一进行比对,如果找到了想要的模式,就返回对应的行,如果没找到就丢弃到该行。此外,我们可以看出来空格在模式匹配的时候也是占一个字符的这就是为什么aline没有出现在结果中。
事实上,正则表达式就是模式匹配的一种规则,有时候我们想要匹配的内容并不是完全一样的,而是具有一定共同规律的字符串,例如love,lover,lovers都有共同的部分love,如果我们要匹配这三种不同的字符串可以直接使用love进行匹配,但是如果我们只想要love,那么就不能仅仅用love来进行限定了,还需要添加一些其他的限制。而正则表达式就能为我们提供一种方便快捷的符号表示方式,来让我们实现更加复杂的一些匹配过程。很多不同的编程语都支持正则表达式,例如R,Python,Perl,Shell等,在Shell中,grep,awk,sed都支持正则匹配,不过正则匹配本身有基础和扩展的部分,现在我们来逐一认识一下它们吧。
BRE(Basic Regular Expression)
^ | 锚定符,锚定字符串开始 |
$ | 锚定符,锚定字符串结束 |
. | d代表除\n以外的任意符号 |
* | 前一个位置的字符出现0次或多次 |
[] | 方括号表达式,该方括号占一个字符的位置,包含该位置可能出现的字符[^]代表取反 |
这五个特殊符号是最常用到的正则表达式符号,看一个例子
cat test2.txt
分别提取包含bam的行和以bam结尾的行
grep 'bam' test2.txt --color
grep 'bam$' test2.txt --color
我们可以通过锚定符来确定我们要的字符串的位置。grep添加--color参数可以高亮匹配到的字符串。
ERE(Extended Regular Expression)
x{n,m} | 匹配前面位置的字符模式最少n次,最多m次 |
| | 交替,指定该位置可以出现竖杠前模式或者竖杠后模式,例如:a|b代表该位置可以是a或者b |
? | 前面指定的字符模式出现0次或者1次,例如a?代表a可以出现0次或者1次 |
+ | 指定前面的字符模式可以出现一次或者多次,但不能不出现 |
() | 括号内的内容被当作一个整体进行匹配,可以结合其他字符进行,例如(abc)|(bcd)代表该位置可以是abc或者bcd ;(abc){2,3}代表abc模式出现至少2次,至多3次 |
扩展正则扩充了正则的功能,我们可以指定匹配模式的个数以及进行分组,()分组是非常重要的一个功能,分组功能可以提升其他正则符号的使用防范和方法,并且衍生出了向后引用的用法。接下来我们从例子中了解一下分组功能带来的改变
cat test3.txt
在grep中使用扩展正则需要添加-E参数,我们分别使用如下两种方式来进行匹配,并且高亮匹配到的部分,看看有什么不同
grep -E '.*\.(txt|tsv|dsv)' test3.txt --color
grep -E '.*\.txt|tsv|dsv' test3.txt --color
虽然搜索的结果是一样的,但是我们发现两次匹配到的字符串部分并不相同,通过()分组符号,我们可以使得其他正则符号的
作用域发生改变,在这个例子中,()使得|符号的作用域从.*\txt变成了txt,进而实现了|符号只对文件名的后缀起作用。分组是正则中很重要的组成部分,它使得我们可以将正则表达式分成多个部分,然后逐个去解决。
接下来展示一个向后引用的例子
下面的例子我们试图寻找到like liker 这样的模式,也就是重用我们之前匹配的部分,为此我们需要先对需要重用的部分增加分组,然后使用\num的方式进行引用,由于这是第一个分组,因此就可以使用\1来引用
cat test5.txt
like liker
love lover
like lover
love liker
grep -E '([a-z]+) \1r' test5.txt
like liker
love lover
最后,我们通过挑选正确邮箱的例子来展示正则表达式的使用
我们要从一些邮箱中挑选出满足以下条件的邮箱
@之前必须有内容且只能是字母(大小写)、数字、下划线(_)、减号(-)、点(.)
@和最后一个点(.)之间必须有内容且只能是字母(大小写)、数字、点(.)、减号(-),且两个点不能挨着
最后一个点(.)之后必须有内容且内容只能是字母(大小写)、数字且长度为大于等于2个字节,小于等于6个字节
可以看下126邮箱注册的界面
这里也可以把红框中的条件加上。
那么这个正则应该怎么来写呢?先看下一个邮箱例子的集合,其中有正确,也有错误邮箱,我们要做的就是提取正确的邮箱
cat test4.txt
888888@qq.com
888888@..com
888888@1234567890.com
888888@,qq.com
abcde_@qq,com
_abcd@126.com
_abc@abc.com.cn
88888@
88888@.abc.com.toolong
grep -P '^[a-zA-Z][\w-.]{5,17}@[\w-]+(\.[\w-]+)*\.[a-zA-Z0-9]{2,6}$' test4.txt
888888@qq.com
888888@1234567890.com
abcde_@qq,com
这里我们使用了grep -P 参数,-P参数指使用perl的正则表达式,在该模式下,我们可以使用\w来代表数字字母下划线,我们借此简化我们的操作。
该正则中值得关注的点是,对于可能出现的部分,我们使用()进行分组并添加*来表示其可能出现也可能不出现的情况,需要注意的是,当我们在正则表达式中想要匹配 ' . '的时候,需要加反斜杠进行转义。
这个正则表达式不严格的地方在于无法过滤掉xxxx@_.com的邮箱地址,这里我们可以稍作修改,如下
grep -P '^[a-zA-Z][\w-.]{5,17}@[a-zA-Z0-9][\w-]*(\.[\w-]+)*\.[a-zA-Z0-9]{2,6}$' test4.txt
最后让我们回顾一下本次博客所设计的关键内容:
- grep模式匹配的基本过程
- 基础正则表达式
- 扩展正则表达式
- 向后引用
- 匹配位于末尾的字符串
- 如何进行有效邮箱的筛选
好的,以上就是本次正则表达式介绍的全部内容,希望我的经验能够带给陌生的你一些帮助。如果有什么问题请留言给我。