正则表达式
一、正则表达式介绍
1.1 什么是正则表达式
简单的说,正则表达式就是为处理大量的字符串及文本而定义的一套规则和方法。
正则表达式就是把人类想要查询的东西,用计算机能识别的语言表达出来的一种规则。
正则表达式在Linux里仅受三剑客(grep,sed,awk)命令支持,其他命令无法使用。
1.2 正则表达式分类
正则表达式:REGEXP,REGular EXPression。
正则表达式分为两类:
- Basic REGEXP(基本正则表达式)
- Extended REGEXP(扩展正则表达式)
二、基本正则表达式
//元字符
. //任意单个字符
[] //匹配指定范围内的任意单个字符
[^] //匹配指定范围外的任意单个字符
//匹配次数(贪婪模式)
* //匹配其前面的任意单个字符任意次
.* //任意长度的任意字符
\? //匹配其前面的任意单个字符1次或0次
\+ //匹配其前面的任意单个字符至少1次
\{m,n\} //匹配其前面的任意单个字符至少m次,至多n次
//位置锚定
^ //锚定行首,此字符后面的任意单个字符必须出现在行首
$ //锚定行尾,此字符前面的任意单个字符必须出现在行尾
^$ //空白行
\<或\b //锚定词首,其后面的任意单个字符必须作为单词首部出现
\>或\b //锚定词尾,其前面的任意单个字符必须作为单词尾部出现
/分组
\(\)
例:\(ab\)*
//后向引用
\1 //引用第一个左括号以及与之对应的右括号所包括的所有内容
\2 //引用第二个左括号以及与之对应的右括号所包括的所有内容
三、扩展正则表达式
简单来说扩展正则表达式支持的符号更多,实现的功能更强大。
//字符匹配
. //匹配任意单个字符
[] //匹配指定范围内的任意单个字符
[^] //匹配指定范围外的任意单个字符
//次数匹配
* //匹配其前面的任意单个字符任意次
? //匹配其前面的任意单个字符1次或0次
+ //匹配其前面的任意单个字符至少1次
{m,n} //匹配其前面的任意单个字符至少m次,至多n次
//位置锚定
^ //锚定行首,此字符后面的任意单个字符必须出现在行首
$ //锚定行尾,此字符前面的任意单个字符必须出现在行尾
^$ //空白行
\<或\b //锚定词首,其后面的任意单个字符必须作为单词首部出现
\>或\b //锚定词尾,其前面的任意单个字符必须作为单词尾部出现
//分组
() //分组
\1,\2,\3,....
例:(ab)*
//后向引用
\1 //引用第一个左括号以及与之对应的右括号所包括的所有内容
\2 //引用第二个左括号以及与之对应的右括号所包括的所有内容
//或者
| //or 默认匹配|的整个左侧或者整个右侧的内容
//例:C|cat表示C或者cat,要想表示Cat或者cat则需要使用分组,如(C|c)at
四、贪婪与懒惰
当正则表达式中包含能接受重复的限定符时,通常的行为是(在使整个表达式能得到匹配的前提下)匹配尽可能多的字符。以这个表达式为例:a.*b
,它将会匹配最长的以a开始,以b结束的字符串
。如果用它来搜索aabab
的话,它会匹配整个字符串aabab
。这被称为贪婪匹配。
有时,我们更需要懒惰匹配,也就是匹配尽可能少的字符。前面给出的限定符都可以被转化为懒惰匹配模式,只要在它后面加上一个问号?
。这样.*?
就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复
。现在看看懒惰版的例子吧:
a.*?b
匹配最短的,以a开始,以b结束的字符串
。如果把它应用于aabab
的话,它会匹配aab(第一到第三个字符)
和ab(第四到第五个字符)
。
表5.懒惰限定符:
代码/语法 | 说明 |
---|---|
*? | 重复任意次,但尽可能少重复 |
+? | 重复1次或更多次,但尽可能少重复 |
?? | 重复0次或1次,但尽可能少重复 |
{n,m}? | 重复n到m次,但尽可能少重复 |
{n,}? | 重复n次以上,但尽可能少重复 |
注:
为什么第一个匹配是aab(第一到第三个字符)而不是ab(第二到第三个字符)?简单地说,因为正则表达式有另一条规则,比懒惰/贪婪规则的优先级更高:最先开始的匹配拥有最高的优先权——The match that begins earliest wins。
五、正则表达式案例
注意:在使用正则表达式时,建议引号使用单引号。
环境准备:
cat > yf.txt << EOF
I am yf!
I teach linux.
test123456789asd123456789
I like play games,basketball,football.
#I like Vegetables Milk
\$my blog is https://www.helloyf.fun/
#baidu site is https://www.baidu.com
my qq is 123456.
my phone is 11111111111.
EOF
- ^ 查找以什么开头的行
[root@localhost ~]# grep '^I' yf.txt
I am yf!
I teach linux.
I like play games,basketball,football.
- $查找以什么结尾的行
[root@localhost ~]# grep '9$' yf.txt
test123456789asd123456789
- ^$排除空行
[root@localhost ~]# grep -v '^$' yf.txt
I am yf!
I teach linux.
test123456789asd123456789
I like play games,basketball,football.
#I like Vegetables Milk
$my blog is https://www.helloyf.fun/
#baidu site is https://www.baidu.com
my qq is 123456.
my phone is 11111111111.
- 匹配单个任意字符
[root@localhost ~]# grep -n '.' yf.txt
1:I am yf!
2:I teach linux.
3:test123456789asd123456789
5:I like play games,basketball,football.
6:#I like Vegetables Milk
7:$my blog is https://www.helloyf.fun/
8:#baidu site is https://www.baidu.com
9:my qq is 123456.
10:my phone is 11111111111.
- \转义字符(将”\“放在特殊字符前面,就会忽略特殊字符的含义为一般字符)
[root@localhost ~]# grep '\.$' yf.txt
I teach linux.
I like play games,basketball,football.
my qq is 123456.
my phone is 11111111111.
- *匹配前一个字符(连续出现)0次或多次
[root@localhost tmp]# touch ab abc abccccc
[root@localhost tmp]# ls | grep 'abc*'
ab
abc
abccccc
- .*匹配所有字符
[root@localhost tmp]# touch {a..c}{1..3}
[root@localhost tmp]# ls | grep '.*'
a1
a2
a3
b1
b2
b3
c1
c2
c3
- [] 匹配[]内包含的任意一个字符
[root@localhost tmp]# touch a b c
[root@localhost tmp]# ls | grep '[abc]'
a
b
c
- [^] 匹配除了[^]内的任意其他字符(取反)
[root@localhost tmp]# touch {a..c}
[root@localhost tmp]# ls
a b c
[root@localhost tmp]# ls | grep '[^a]'
b
c
[root@localhost ~]# grep '[^.]$' yf.txt
I am yf!
test123456789asd123456789
#I like Vegetables Milk
$my blog is https://www.helloyf.fun/
#baidu site is https://www.baidu.com
[root@localhost ~]# grep '[^.!]$' yf.txt
test123456789asd123456789
#I like Vegetables Milk
$my blog is https://www.helloyf.fun/
#baidu site is https://www.baidu.com
[root@localhost ~]# grep '^#.*[^.!]$' yf.txt
#I like Vegetables Milk
#baidu site is https://www.baidu.com
- +匹配前面一个字符1次或多次
\\基础正则
[root@localhost ~]# grep 'w\+' yf.txt
$my blog is https://www.helloyf.fun/
#baidu site is https://www.baidu.com
\\扩展正则
[root@localhost ~]# grep -e 'w+' yf.txt
[root@localhost ~]# egrep 'w+' yf.txt
$my blog is https://www.helloyf.fun/
#baidu site is https://www.baidu.com
- ?匹配前面一个字符0次或一次
[root@localhost tmp]# touch a ab abb abbb
[root@localhost tmp]# ls
a ab abb abbb
[root@localhost tmp]# ls | egrep '^ab?$'
a
ab
- | 或者
[root@localhost ~]# egrep '^I|\.$' yf.txt
I am yf!
I teach linux.
I like play games,basketball,football.
my qq is 123456.
my phone is 11111111111.
- () 分组
[root@localhost ~]# cat test.txt
hello world! Today is Wednesday.
hello Stranger! Have you eaten?
//sed的-r选项是启用扩展正则
[root@localhost ~]# sed -r 's/(hel+o) (.*) (.*)/\3 \1 \2/' test.txt
Wednesday. hello world! Today is
eaten? hello Stranger! Have you
//照上面的例子可以看到‘hello’是第一组,‘world! Today is’是第二组 \
‘Wednesday.’是第三组。为什么第二组有这么多个呢?是因为“贪婪模式”
- {} 匹配前面一个字符的次数
- {num} :{5} 匹配前一个字符五次
- {num1,}:{8,} 匹配前一个字符最少八次
- {num1,num2}:{3,8} 匹配前一个字符最少三次,最多八次
[root@localhost ~]# egrep '[0-9]{11}' yf.txt
my phone is 11111111111.
[root@localhost ~]# egrep '[0-9]{8,}' yf.txt
test123456789asd123456789
my phone is 11111111111.
[root@localhost ~]# egrep '[0-9]{6,10}' yf.txt
test123456789asd123456789
my qq is 123456.
my phone is 11111111111.
\<
或\b
锚定词首
[root@localhost ~]# egrep '\<w' test.txt
hello world! Today is Wednesday.
[root@localhost ~]# egrep '\bH' test.txt
hello Stranger! Have you eaten?
\>
或\b
锚定词尾
[root@localhost ~]# egrep 'y\>' test.txt
hello world! Today is Wednesday.
[root@localhost ~]# egrep 'n\b' test.txt
hello Stranger! Have you eaten?