正则表达式
1、纯文本
$ echo "This is a test" | sed -n '/test/p'
This is a test
$ echo "This is a test" | sed -n '/trial/p'
$
$ echo "This is a test" | gawk '/test/{print $0}'
This is a test
$ echo "This is a test" | gawk '/trial/{print $0}'
第一个模式定义了一个单词test。 sed编辑器和gawk程序脚本用它们各自的print命令打印出
匹配该正则表达式模式的所有行。由于echo语句在文本字符串中包含了单词test,数据流文本能
够匹配所定义的正则表达式模式,因此sed编辑器显示了该行。
2、可以在正则表达式中使用空格和数字
在正则表达式中,空格和其他的字符并没有什么区别。
$ echo "This is line number1" | sed -n '/ber 1/p'
如果你在正则表达式中定义了空格,那么它必须出现在数据流中。甚至可以创建匹配多个连
续空格的正则表达式模式。
$ cat data1
This is a normal line of text.
This is a line with too many spaces.
$ sed -n '/ /p' data1
This is a line with too many spaces.
单词间有两个空格的行匹配正则表达式模式。这是用来查看文本文件中空格问题的好办法。
3、特殊字符
正则表达式中的特殊字符:.*[]^${}+?|()
如果要查找文本中的美元符,只要在它前面加个反斜线。
$ cat data2
The cost is $4.00
$ sed -n '/\$/p' data2
The cost is $4.00
由于反斜线是特殊字符,如果要在正则表达式模式中使用它,你必须对其转义,这样就产生
了两个反斜线。
$ echo "\ is a special character" | sed -n '/\\/p'
\ is a special character
最终,尽管正斜线不是正则表达式的特殊字符,但如果它出现在sed编辑器或gawk程序的正
则表达式中,你就会得到一个错误。
$ echo "3 / 2" | sed -n '///p'
sed: -e expression #1, char 2: No previous regular expression
要使用正斜线,也需要进行转义。
$ echo "3 / 2" | sed -n '/\//p'
3 / 2
现在sed编辑器能正确解释正则表达式模式了,一切都很顺利。
4、锚字符
4.1、锁定在行首
脱字符会在每个由换行符决定的新数据行的行首检查模式。
$ cat data3
This is a test line.
this is another test line.
A line that tests this feature.
Yet more testing of this
$ sed -n '/^this/p' data3
this is another test line.
只要模式出现在新行的行首,脱字符就能够发现它。
如果你将脱字符放到模式开头之外的其他位置,那么它就跟普通字符一样,不再是特殊字符了:
$ echo "This ^ is a test" | sed -n '/s ^/p'
This ^ is a test
由于脱字符出现在正则表达式模式的尾部, sed编辑器会将它当作普通字符来匹配。
4.2、锁定在行尾
跟在行首查找模式相反的就是在行尾查找。特殊字符美元符( $)定义了行尾锚点。将这个
特殊字符放在文本模式之后来指明数据行必须以该文本模式结尾。
$ echo "This is a good book" | sed -n '/book$/p'
This is a good book
$ echo "This book is good" | sed -n '/book$/p'
4.3、组合使用
第二种情况乍一看可能有些怪异,但极其有用。将两个锚点直接组合在一起,之间不加任何
文本,这样过滤出数据流中的空白行。考虑下面这个例子。
$ 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.
5、点号字符
来看一些在正则表达式模式中使用点号字符的例子。
$ 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.
你应该能够明白为什么第一行无法匹配,而第二行和第三行就可以。第四行有点复杂。注意,
我们匹配了at,但在at前面并没有任何字符来匹配点号字符。其实是有的!在正则表达式中,
空格也是字符,因此at前面的空格刚好匹配了该模式。第五行证明了这点,将at放在行首就不
会匹配该模式了。
6、字符组
下面是个创建字符组的例子。
$ sed -n '/[ch]at/p' data6
The cat is sleeping.
That is a very nice hat.
这里用到的数据文件和点号特殊字符例子中的一样,但得到的结果却不一样。这次我们成功
滤掉了只包含单词at的行。匹配这个模式的单词只有cat和hat。还要注意以at开头的行也没有
匹配。字符组中必须有个字符来匹配相应的位置。
在不太确定某个字符的大小写时,字符组会非常有用。
$ echo "Yes" | sed -n '/[Yy]es/p'
Yes
$ echo "yes" | sed -n '/[Yy]es/p'
yes
可以在单个表达式中用多个字符组。
$ echo "Yes" | sed -n '/[Yy][Ee][Ss]/p'
Yes
$ echo "yEs" | sed -n '/[Yy][Ee][Ss]/p'
yEs
$ echo "yeS" | sed -n '/[Yy][Ee][Ss]/p'
yeS
正则表达式使用了3个字符组来涵盖了3个字符位置含有大小写的情况。
字符组不必只含有字母,也可以在其中使用数字。
$ 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.
这个正则表达式模式匹配了任意含有数字0、 1、 2或3的行。含有其他数字以及不含有数字的行都会被忽略掉。
7、排除型字符组
$ 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 '/[^ch]at/p' data6
This test is at line four.
通过排除型字符组,正则表达式模式会匹配c或h之外的任何字符以及文本模式。由于空格字符属于这个范围,它通过了模式匹配。但即使是排除,字符组仍然必须匹配一个字符,所以以at开头的行仍然未能匹配模式。
8、区间
通过指定数字区间来简化邮编的例子。
$ sed -n '/^[0-9][0-9][0-9][0-9][0-9]$/p' data8
60633
46201
45902
这样可是节省了不少的键盘输入!每个字符组都会匹配0~9的任意数字。如果字母出现在数据中的任何位置,这个模式都将不成立。
$ echo "a8392" | sed -n '/^[0-9][0-9][0-9][0-9][0-9]$/p'
$
$ echo "1839a" | sed -n '/^[0-9][0-9][0-9][0-9][0-9]$/p'
$
$ echo "18a92" | sed -n '/^[0-9][0-9][0-9][0-9][0-9]$/p'
同样的方法也适用于字母。
$ sed -n '/[c-h]at/p' data6
The cat is sleeping.
That is a very nice hat.
新的模式[c-h]at匹配了首字母在字母c和字母h之间的单词。这种情况下,只含有单词at的行将无法匹配该模式。
还可以在单个字符组指定多个不连续的区间。
$ sed -n '/[a-ch-m]at/p' data6
The cat is sleeping.
That is a very nice hat.
该字符组允许区间a~c、 hm中的字母出现在at文本前,但不允许出现dg的字母。434 第 20 章 正则表达式
$ echo "I'm getting too fat." | sed -n '/[a-ch-m]at/p'
该模式不匹配fat文本,因为它没在指定的区间。
9、特殊的字符组
[[: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
$ echo "This is a test" | sed -n '/[[:punct:]]/p'
10、星号
在字符后面放置星号表明该字符必须在匹配模式的文本中出现0次或多次。
$ echo "ik" | sed -n '/ie*k/p'
ik
$ echo "iek" | sed -n '/ie*k/p'
iek
$ echo "ieek" | sed -n '/ie*k/p'
ieek
$ echo "ieeek" | sed -n '/ie*k/p'20.2 定义 BRE 模式 435
ieeek
$ echo "ieeeek" | sed -n '/ie*k/p'
ieeeek
11、问号
问号类似于星号,不过有点细微的不同。问号表明前面的字符可以出现0次或1次,但只限于此。它不会匹配多次出现的字符。
$ echo "bt" | gawk '/be?t/{print $0}'
bt
$ echo "bet" | gawk '/be?t/{print $0}'
bet
$ echo "beet" | gawk '/be?t/{print $0}'
$ echo "beeet" | gawk '/be?t/{print $0}'
12、加号
加号是类似于星号的另一个模式符号,但跟问号也有不同。加号表明前面的字符可以出现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}'
如果字符e没有出现,模式匹配就不成立。加号同样适用于字符组,与星号和问号的使用方式相同。
$ echo "bt" | gawk '/b[ae]+t/{print $0}'
$
$ echo "bat" | gawk '/b[ae]+t/{print $0}'
bat
$ echo "bet" | gawk '/b[ae]+t/{print $0}'
bet
$ echo "beat" | gawk '/b[ae]+t/{print $0}'
beat
$ echo "beet" | gawk '/b[ae]+t/{print $0}'
beet
$ echo "beeat" | gawk '/b[ae]+t/{print $0}'
beeat
这次如果字符组中定义的任一字符出现了,文本就会匹配指定的模式。
13、使用花括号
这里有个使用简单的单值间隔的例子。
$ echo "bt" | gawk --re-interval '/be{1}t/{print $0}'
$438 第 20 章 正则表达式
$ echo "bet" | gawk --re-interval '/be{1}t/{print $0}'
bet
$ echo "beet" | gawk --re-interval '/be{1}t/{print $0}'
通过指定间隔为1,限定了该字符在匹配模式的字符串中出现的次数。如果该字符出现多次,模式匹配就不成立。很多时候,同时指定下限和上限也很方便。
$ echo "bt" | gawk --re-interval '/be{1,2}t/{print $0}'
$
$ echo "bet" | gawk --re-interval '/be{1,2}t/{print $0}'
bet
$ echo "beet" | gawk --re-interval '/be{1,2}t/{print $0}'
beet
$ echo "beeet" | gawk --re-interval '/be{1,2}t/{print $0}'
在这个例子中,字符e可以出现1次或2次,这样模式就能匹配;否则,模式无法匹配。
间隔模式匹配同样适用于字符组。
$ echo "bt" | gawk --re-interval '/b[ae]{1,2}t/{print $0}'
$
$ echo "bat" | gawk --re-interval '/b[ae]{1,2}t/{print $0}'
bat
$ echo "bet" | gawk --re-interval '/b[ae]{1,2}t/{print $0}'
bet
$ echo "beat" | gawk --re-interval '/b[ae]{1,2}t/{print $0}'
beat
$ echo "beet" | gawk --re-interval '/b[ae]{1,2}t/{print $0}'
beet
$ echo "beeat" | gawk --re-interval '/b[ae]{1,2}t/{print $0}'
$
$ echo "baeet" | gawk --re-interval '/b[ae]{1,2}t/{print $0}'
$
$ echo "baeaet" | gawk --re-interval '/b[ae]{1,2}t/{print $0}'
如果字母a或e在文本模式中只出现了1~2次,则正则表达式模式匹配;否则,模式匹配失败。
14、管道符号
管道符号允许你在检查数据流时,用逻辑OR方式指定正则表达式引擎要用的两个或多个模式。如果任何一个模式匹配了数据流文本,文本就通过测试。如果没有模式匹配,则数据流文本匹配失败。
使用管道符号的格式如下:
expr1|expr2|...
这里有个例子。
$ echo "The cat is asleep" | gawk '/cat|dog/{print $0}'20.4 正则表达式实战 439
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或dog。正则表达式和管道符号之间不能有空格,否则它们也会被认为是正则表达式模式的一部分。管道符号两侧的正则表达式可以采用任何正则表达式模式(包括字符组)来定义文本。
$ echo "He has a hat." | gawk '/[ch]at|dog/{print $0}'
He has a hat.
这个例子会匹配数据流文本中的cat、 hat或dog。\
15、表达式分组
正则表达式模式也可以用圆括号进行分组。当你将正则表达式模式分组时,该组会被视为一
个标准字符。可以像对普通字符一样给该组使用特殊字符。举个例子:
$ 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}'
模式(c|b)a(b|t)会匹配第一组中字母的任意组合以及第二组中字母的任意组合。