关于linux下使用grep和egrep中单引号‘’、双引号“”、小括号()和大括号{}的一些问题

时间:2022-01-01 19:34:44

最近在linux下使用grep和egrep发现了一些问题。以前一直以为egrep包含了grep,因此grep中采用的格式,在egrep中能得到相同的结果。其实这个想法是不对的。


原因主要是在于正则表达式和扩展正则表达式的一些元符号上的问题,在这篇帖子(http://www.linuxidc.com/Linux/2014-03/99152.htm)中我找到了答案:

BRE与ERE在能力上区别仅在多项匹配的能力上,其他方面没有大的差别,主要的区别体现在元字符上。

BRE只定义了4组元字符:

[] 用于在多个字符中选定一个字符进行匹配,[]内可以有-以示范围,但-本身不是元字符

. 用于匹配任意字符

^ 用于匹配时表示“非”的含义,还有一个用法是匹配行首

$ 用于匹配行尾

ERE在此基础上增加了3组元字符的定义:

{} 用于表示重复匹配的次数。BRE中只将{}当作普通字符对待,要使用此功能必须加\进行转义,即“\{\}”

() 用于分组。BRE中只将()当作普通字符对待,要使用此功能必须加\进行转义,即“\(\)”

| 完全为ERE新增的多项匹配能力定义的,BRE无多项匹配能力,只将|作普通字符对待


在这里我插一句:我最开始看正则表达式,是鸟哥的linux私房菜基础篇第三版上讲正则的那一块儿。后面发现问题再回去看,发现有个地方写的不清楚,导致我很多试验结果不太对。后来看到上面博客中写的,我才明白。鸟哥这里写的可能表述有点小问题。里面在介绍BRE中采用大括号表示匹配次数中,如下图中例题五那儿加粗的解释:

关于linux下使用grep和egrep中单引号‘’、双引号“”、小括号()和大括号{}的一些问题


然而,因为是BRE,因此必须采用\{ \} ,而不是{}。这并不是因为shell中对{}有特殊含义,才使用的转义\。因为鸟哥在写的时候已经加上单引号''了,shell不会再对单引号里面的进行转义了。(但是如果不加引号,确实需要再转义,此时应该写成\\\{ 和\\\},前面一对\\是产生\,后面一对\{是产生{)因此,在linux下还有可能受到‘’和“”的影响,因此,在linux下采用grep或者egrep时,应该注意:

1、当pattern或者说匹配模式中,没有空格,可以不用引号,但是有空格一定要用引号。

2、考虑单引号和双引号的区别,考虑pattern是否有变量,是否需要求值

3、采用的是BRE还是ERE(这在采用小括号()和大括号{}的时候非常重要)



我们看几个例子:

测试文本是demo.txt

里面内容是:

This
Thi
This Thi
aaThiaa
(a)
a{10}
abcedefghi

一、找出一个由三个字母构成的单词,该单词由Th开头

当然我们可以这样写

grep -n '\bTh.\b' demo.txt

关于linux下使用grep和egrep中单引号‘’、双引号“”、小括号()和大括号{}的一些问题

现在我们换个思路,一个单词,前面除非为一行开头或者空格,后面为行结尾或者空格,那么我们可以这么写:

grep -nE '(^|[[:space:]])Th.($|[[:space:]])' demo.txt 
或者 

egrep -n '(^|[[:space:]])Th.($|[[:space:]])' demo.txt  
这里一定要用ERE,因为有 | 这个元符号


结果如下:

关于linux下使用grep和egrep中单引号‘’、双引号“”、小括号()和大括号{}的一些问题


二、找到由十个字母构成的单词,这里不考虑一种关于单词的限定,我们只要区别于demo.txt中后面的两行

如果我们用grep

应该写成:

 grep -n '[a-z]\{10\}' demo.txt 
结果如下:

关于linux下使用grep和egrep中单引号‘’、双引号“”、小括号()和大括号{}的一些问题


如果写成:

grep -n '[a-z]{10}' demo.txt 
结果如下:
关于linux下使用grep和egrep中单引号‘’、双引号“”、小括号()和大括号{}的一些问题 关于linux下使用grep和egrep中单引号‘’、双引号“”、小括号()和大括号{}的一些问题

因此,在BRE中,大括号{}默认就是本身,如果需要使用表示匹配字符的格式,应该用\{ \}

如果,我们在linux shell中,写grep时不用单引号,应该这么写:

grep -n [a-z]\\\{10\\\} demo.txt

关于linux下使用grep和egrep中单引号‘’、双引号“”、小括号()和大括号{}的一些问题关于linux下使用grep和egrep中单引号‘’、双引号“”、小括号()和大括号{}的一些问题

这是因为shell先把命令读进去,然后把 \\ 变成 \,把 \{ 变成{, 所以才能形成\{。因此在引号里面写成\{并不是shell的原因,是BRE本身的语法规则,如果不加引号,才需要考虑shell可能有一些特殊字符需要转义。如果要匹配第6行,就要写成:

grep -n [a-z]\{10\} demo.txt
关于linux下使用grep和egrep中单引号‘’、双引号“”、小括号()和大括号{}的一些问题关于linux下使用grep和egrep中单引号‘’、双引号“”、小括号()和大括号{}的一些问题

所以,我觉得,在linux shell 中使用grep 时 pattern部分加上引号更加方便。


如果我们使用egrep

 egrep -n '[a-z]{10}' demo.txt 

关于linux下使用grep和egrep中单引号‘’、双引号“”、小括号()和大括号{}的一些问题关于linux下使用grep和egrep中单引号‘’、双引号“”、小括号()和大括号{}的一些问题

因为此时是ERE,语法上就是直接使用{},如果要匹配第6行,应该是

egrep -n '[a-z]\{10\}' demo.txt
关于linux下使用grep和egrep中单引号‘’、双引号“”、小括号()和大括号{}的一些问题

关于linux下使用grep和egrep中单引号‘’、双引号“”、小括号()和大括号{}的一些问题

如果不加引号,应该这么写:

egrep -n [a-z]\{10\} demo.txt 
关于linux下使用grep和egrep中单引号‘’、双引号“”、小括号()和大括号{}的一些问题

关于linux下使用grep和egrep中单引号‘’、双引号“”、小括号()和大括号{}的一些问题

如果不加引号,要匹配第6行,应该是

 egrep -n [a-z]\\\{10\\\} demo.txt 
关于linux下使用grep和egrep中单引号‘’、双引号“”、小括号()和大括号{}的一些问题

关于linux下使用grep和egrep中单引号‘’、双引号“”、小括号()和大括号{}的一些问题

我们可以看出,在上面grep和egrep中的例子中,结果正好是对调的,这主要是因为BRE和ERE语法中元符号的形式的原因,因此egrep和grep在写的时候一定考虑好模式的表达。

除此以外,还要注意linux shell中单引号‘’ 和双引号“”的区别。