正则表达式:定义的模式模板(pattern template),Linux工具可以用它来过滤文本。
如果匹配了定义的模式,它就会被接受并进一步处理;如果数据不匹配模式,它就会被滤掉。
在Linux中,有两种流行的正则表达式引擎:
POSIX基础正则表达式(basic regular expression, BRE)引擎
POSIX扩展正则表达式(extended regular expression, ERE)引擎
BRE模式--纯文本
正则表达式第一条原则就是:'正则表达式模式都区分大小写。
$ echo "The books are expensive" | sed -n '/book/p'
The books are expensive
BRE模式--特殊字符
正则表达式识别的特殊字符包括:
.*[]^${}\+?|()
特殊字符作为文本字符,就必须转义,转义字符:是反斜线(\)。
$ echo "3 / 2" | sed -n '/\//p'
3 / 2
BRE模式--锚字符
- 锁定在行首
脱字符(^)定义从数据流中文本行的行首开始的模式。
$ echo "This ^ is a test" | sed -n '/s ^/p'
This ^ is a test
// 如果指定正则表达式模式时只用了脱字符,就不需要用反斜线来转义。但如果你在模式中先指定了脱字符,随后还有其他一些文本,那么你必须在脱字符前用转义字符。
- 锁定在行尾
特殊字符美元符($)定义了行尾锚点。
$ echo "This is a good book" | sed -n '/book$/p'
This is a good book
// 要想匹配,文本模式必须是行的最后一部分。比如 sed -n '/boo$/p' 就不能实现匹配
- 组合锚点
行首和行尾同时使用。
$ cat data5
This is one test line.
This is another test line.
$ sed '/^$/d' data5
This is one test line.
This is another test line.
BRE模式--点号字符
特殊字符点号用来匹配除换行符之外的任意单个字符。它必须匹配一个字符,如果在点号字符的位置没有字符,那么模式就不成立。
$ cat data6
This is a test of a line.
The cat is sleeping.
That is a very nice hat.
This test is at line four.
at ten o'clock we'll go home.
$ sed -n '/.at/p' data6
The cat is sleeping.
That is a very nice hat.
This test is at line four.
// 'cat' 'hat' ' at' 都可以匹配;而'a t'不能匹配。
BRE模式--字符组
字符组中必须有个字符来匹配相应的位置。
$ cat data7
This line doesn't contain a number.
This line has 1 number on it.
This line a number 2 on it.
This line has a number 4 on it.
$ sed -n '/[0123]/p' data7
This line has 1 number on it.
This line a number 2 on it.
如果要确保只匹配五位数,就必须将匹配的字符和其他字符分开,要么用空格,要么指明它们就在行首和行尾。
$ sed -n '
> /^[0123456789][0123456789][0123456789][0123456789][0123456789]$/p
> ' data8
60633
46201
22203
// 过滤出data8中只有5个字符的邮编。
字符组的一个极其常见的用法是"解析拼错的单词",比如用户表单输入的数据。
$ cat data9
I need to have some maintenence done on my car.
I'll pay that in a seperate invoice.
After I pay for the maintenance my car will be as good as new.
$ sed -n '
> /maint[ea]n[ae]nce/p
> /sep[ea]r[ea]te/p
> ' data9
I need to have some maintenence done on my car.
I'll pay that in a seperate invoice.
After I pay for the maintenance my car will be as good as new.
BRE模式--排除型字符组
可以寻找组中没有的字符,而不是去寻找组中含有的字符。
$ sed -n '/[^ch]at/p' data6
This test is at line four.
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
$ echo "abc" | sed -n '/[[:digit:]]/p'
$
$ echo "abc" | sed -n '/[[:alpha:]]/p'
abc
$ echo "abc123" | sed -n '/[[:digit:]]/p'
abc123
$ echo "This is, a test" | sed -n '/[[:punct:]]/p'
This is, a test
BRE模式--区间
可以用单破折线符号在字符组中表示字符区间。
$ sed -n '/^[0-9][0-9][0-9][0-9][0-9]$/p' data8
60633
46201
45902
也适用于字母。
$ sed -n '/[c-h]at/p' data6
The cat is sleeping.
That is a very nice hat.
可以在单个字符组指定多个不连续的区间。
$ sed -n '/[a-ch-m]at/p' data6
The cat is sleeping.
That is a very nice hat.
BRE模式--星号
在字符后面放置星号表明该字符必须在匹配模式的文本中出现0次或多次。
$ echo "I ate a potatoe with my lunch." | sed -n '/potatoe*/p'
I ate a potatoe with my lunch.
将点号特殊字符和星号特殊字符组合起来。这个组合能够匹配任意数量的任意字符。它通常用在数据流中两个可能相邻或不相邻的文本字符串之间。
$ echo "this is a regular pattern expression" | sed -n '
> /regular.*expression/p'
this is a regular pattern expression
扩展--问号
问号表明前面的字符可以出现0次或1次,但只限于此。它不会匹配多次出现的字符。
$ echo "bt" | gawk '/be?t/{print $0}'
bt
$ echo "bet" | gawk '/be?t/{print $0}'
bet
扩展--加号
加号表明前面的字符可以出现1次或多次,但必须至少出现1次。如果该字符没有出现,那么模式就不会匹配。
$ echo "beeet" | gawk '/be+t/{print $0}'
beeet
$ echo "beet" | gawk '/be+t/{print $0}'
beet
$ echo "bet" | gawk '/be+t/{print $0}'
bet
$ echo "bt" | gawk '/be+t/{print $0}'
$
扩展--花括号
间隔(interval),可以用两种格式来指定区间。
m:正则表达式准确出现m次。
m, n:正则表达式至少出现m次,至多n次。
这个特性可以精确调整字符或字符集在模式中具体出现的次数。
// 默认情况下, gawk程序不会识别正则表达式间隔。必须指定gawk程序的--re- interval命令行选项才能识别正则表达式间隔。
$ echo "bt" | gawk --re-interval '/be{1}t/{print $0}'
$
$ echo "bet" | gawk --re-interval '/be{1}t/{print $0}'
bet
$ echo "beet" | gawk --re-interval '/be{1}t/{print $0}'
$
字符e可以出现1次或2次,这样模式就能匹配;否则,模式无法匹配:
$ echo "bet" | gawk --re-interval '/be{1,2}t/{print $0}'
bet
$ echo "beet" | gawk --re-interval '/be{1,2}t/{print $0}'
beet
扩展--管道符号
管道符号允许你在检查数据流时,用逻辑OR方式指定正则表达式引擎要用的两个或多个模式。
使用管道符号的格式如下:
expr1|expr2|...
$ echo "The cat is asleep" | gawk '/cat|dog/{print $0}'
The cat is asleep
$ echo "The dog is asleep" | gawk '/cat|dog/{print $0}'
The dog is asleep
$ echo "The sheep is asleep" | gawk '/cat|dog/{print $0}'
$
这个例子会匹配数据流文本中的cat、 hat或dog。
$ echo "He has a hat." | gawk '/[ch]at|dog/{print $0}'
He has a hat.
扩展--表达式分组
可以用圆括号()进行分组。
$ echo "Sat" | gawk '/Sat(urday)?/{print $0}'
Sat
$ echo "Saturday" | gawk '/Sat(urday)?/{print $0}'
Saturday
结尾的urday分组以及问号,使得模式能够匹配完整的Saturday或缩写Sat。
将分组和管道符号一起使用来创建可能的模式匹配组是很常见的做法:
$ echo "cat" | gawk '/(c|b)a(b|t)/{print $0}'
cat
$ echo "cab" | gawk '/(c|b)a(b|t)/{print $0}'
cab
$ echo "bat" | gawk '/(c|b)a(b|t)/{print $0}'
bat
$ echo "bab" | gawk '/(c|b)a(b|t)/{print $0}'
bab
$ echo "tab" | gawk '/(c|b)a(b|t)/{print $0}'
$
$ echo "tac" | gawk '/(c|b)a(b|t)/{print $0}'
$
总结与补充
基本正则
grep 'partern'
扩展正则
grep -E 'partern'
perl正则(更强的正则)
grep -P 'partern'
1.必记的常用元字符:
.匹配除换行符以外的任意字符
\w 匹配字母或数字或下划线或汉字 \W表示与之相反
\s匹配任意的空白符\S表示与之相反
\d匹配数字\D表示与之相反
\b匹配单词的开始或结束\B表示与之相反
^匹配字符串的开始
$匹配字符串的结束
[x] 匹配字符x [^x]表示出来字符x以外的任意字符
[aeiou] 匹配英语中的元音字母 [^aeiou]表示除了元音字母以外的所有字符
实例
[root@hadoop ~]# cat test.txt
hello
998
@#$
44.5
nihao ll
[root@hadoop ~]# cat test.txt | grep -P '\w'
hello
998
44.5
nihao ll
[root@hadoop ~]# cat test.txt | grep -P '\W'
@#$
44.5
nihao ll
[root@hadoop ~]# cat test.txt | grep -P '\s'
nihao ll
[root@hadoop ~]# cat test.txt | grep -P '\S'
hello
998
@#$
44.5
nihao ll
[root@hadoop ~]# cat test.txt | grep -P '\d'
998
44.5
[root@hadoop ~]# cat test.txt | grep -P '\D'
hello
@#$
44.5
nihao ll
[root@hadoop ~]# cat test.txt | grep -P '\b'
hello
998
44.5
nihao ll
[root@hadoop ~]# cat test.txt | grep -P '\B'
hello
998
@#$
44.5
nihao ll
[root@hadoop ~]# cat test.txt | grep -P '^$'
[root@hadoop ~]#
2.必记的常用限定符
*重复零次或更多次
+重复一次或更多次
? 重复零次或一次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次
3.必记的常用分组语法
小括号表示一个子表达式,匹配这个子表达式也就是捕获匹配到的文本,默认情况下,每个分组会自动拥有一个组号,
规则是:从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推,
组号分配过程是要从左向右扫描两遍的:第一遍只给未命名组分配,第二遍只给命名组分配--因此所有命名组的组号都大于未命名的组号
3.1捕获
(exp) 匹配exp,并捕获文本到自动命名的组里 \b(\w+)\b\s+\1\b可以用来匹配重复的单词,像go go, 或者kitty kitty
[root@hadoop ~]# echo 'kitty kitty' | grep -oE '\b(\w+)\b\s+\1\b'
kitty kitty
(?<name>exp)匹配exp,并捕获文本到名称为name的组里,也可以写成(?'name'exp)
上面的正则指定组名,可以写成\b(?<Word>\w+)\b\s+\k<Word>\b
(?:exp) 匹配exp,不捕获匹配的文本,也不给此分组分配组号
[root@hadoop ~]# echo 'kitty kitty' | grep -oP '\b(?<wox>\w+)\b\s+\k<wox>\b'
kitty kitty
(?:@\w)binguo\w匹配@开头包含binguo的字符串
(?:exp)不会改变正则表达式的处理方式,只是这样的组匹配的内容不会像前两种那样被捕获到某个组里面,也不会拥有组号
3.2零宽断言
(?=exp) 匹配exp前面的位置
\b\w+(?=ing\b) I'm singing while you're dancing 匹配sing和danc
(?<=exp)匹配exp后面的位置
(?<=\bre)\w+\b reading a book 匹配ading
[root@hadoop ~]# ifconfig | grep -oP '(?<=inet addr:).*(?=Bcast)'
192.168.121.128
[root@hadoop ~]# ifconfig | grep -oP '(?=inet addr:).*(?<=Bcast)'
inet addr:192.168.121.128 Bcast
[root@hadoop ~]# ifconfig | grep -oP '(?<=inet addr:).*(?<=Bcast)'
192.168.121.128 Bcast
(?<=\s)\d+(?=\s)匹配以空白符间隔的数字(再次强调,不包括这些空白符)
[root@hadoop ~]# echo "nihao 9527 hello" | grep -P '(?<=\s)\d+(?=\s)'
nihao 9527 hello
(?!exp) 匹配后面跟的不是exp的位置
需求:获取不是.exe后缀文件不含后缀的文件名
[root@hadoop ~]# cat files.test
notexefile1.txt
exefile1.exe
exefile2.exe
exefile3.exe
notexefile2.php
notexefile3.sh
[root@hadoop ~]# cat files.test | grep -oP '(.+)(?!\.exe)\.[\S]+$'
notexefile1.txt
notexefile2.php
notexefile3.sh
(?<!exp)匹配前面不是exp的位置
需求:获取不是name参数的值
[root@hadoop ~]# cat form.txt
name=zhangsan
password=123456
---------------
age=26
sex=man
[root@hadoop ~]# cat form.txt | grep -oP '^[^=]+=(?<!name=)(.+)'
password=123456
age=26
sex=man
3.3注释
(?#comment)这种类型的分组不对正则表达式的处理产生任何影响,用于提供注释让人阅读
4.必记的懒惰限定符
*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
贪婪匹配:在使整个表达式能得到匹配的前提下,匹配尽可能多的字符
a.*b,它将会匹配最长的以a开始,以b结束的字符串。如果用它来搜索aabab的话,它会匹配整个字符串aabab。
懒惰(非贪婪)匹配:
也就是匹配尽可能少的字符,《2.必记的常用限定符》提到的限定符都可以被转化为懒惰匹配模式,只要在它后面加上一个问号?
a.*?b,匹配最短的,以a开始,以b结束的字符串。
如果把它应用于aabab的话,它会匹配aab(第一到第三个字符)和ab(第四到第五个字符)
为什么第一个匹配的是aab(第一到第三个字符)而不是ab(第二到第三个字符)?
简单地说,因为正则表达式有另一条规则,比懒惰/贪婪规则的优先级更高:最先开始的匹配拥有最高的优先权
[root@hadoop ~]# echo '<div>test1</div>bb<div>test2</div>' | grep -oP '<div>.*</div>'
<div>test1</div>bb<div>test2</div>
[root@hadoop ~]# echo '<div>test1</div>bb<div>test2</div>' | grep -oP '<div>.*?</div>'
<div>test1</div>
<div>test2</div>
闯关模式实践
http://regex.alf.nu 闯关模式练习正则表达式,完成一个个正则匹配的测验
http://regexone.com/ 通过实际练习掌握正则表达式
https://regexcrossword.com/ 正则挑战,有不同难度,很丰富
http://callumacrae.github.io/regex-tuesday/ 正则挑战,完成正则匹配要求