1.3 文本匹配
一些unix的文本处理程序让你可以搜索,某些时候可以改变一些文本模式,而不是固定的字串。这些应用包括编辑工具ed,ex,vi和sed,还有awk程序语言和命令行命令grep和egrep.文本模式(更正式地讲应该叫正则表达式)包含了平常的字符与一系列的特殊字符(称为元字符)
1.3.1 文件名对模式
在模式匹配中使用的元字符与在文件名扩展的元字符是不同的。当你在命令行发出一个命令的时候,特殊字符在shell中首先被看到,然后才是程序,所以没有被引号引起来的元字符被shell中断修改了扩展部分。例如命令:
$grep [A-Z]* chap[12]
不能被shell转换为:
$grep Array.c Bug.c Comp.c chap1 chap2
这样就开始在文件Bug.c Comp.c,chap1,chap2中查找Array.c字串。为了通过shell并把特殊字符传递给grep,要像以下这样使用引号:
$grep "[A-Z]*" chap[12]
双引号在大部分情况下就足够了,但是单引号是最安全的用法。
注意在模式匹配中,"?"会匹配0个或1个正则表达式的实例;在文件名扩展中,?匹配一个字符。
1.3.2 元字符
同的元字符有不同的意思,这样根它们在什么地方用有关系。特别地,正则表达式被用来在文本中进行搜索,同时元字符在处理有不同的集的文本替换上常被使用。这些集之间互相也不相同,这一节就讲述了这些元字符在搜索和替换的时候是如何使用的,并说明了不同的变量在不同的应用程序中的具体的描述。
1.3.2.1 搜索模式
在下面的表中的字符只在搜索模式中有特殊的意义:
字符 模式
. 匹配除去新行之外的任意单个字符。在awk中可以匹配空行
* 匹配任何(或没有)个在它之前的字符。之前的字符可以是一个正则的表达式。比如说,因为.可以表示任何字符,则.*就表示"匹配任何数量的任意字符"
^ 在一行或一个字符串的开始部分匹配之后的正则表达式
$ 在一行或一个字符串的尾部部分匹配之前的正则表达式
\ 取消下一个字符的特殊意义
[] 匹配任何一个用括号括着的字符。连字符"-"表示了连续的一个字符的范围。^符号表示了匹配任何一个不在括号内的字符在第一个字符位置为一个连字符或反括号被认为是列表中的一个元素。所有的元字符都被认为是列表中的一个成员
{n,m} 匹配在之前的一个字符的出现范围。之前的字符也可以是元字符,{n}匹配了正好出现n次;{n,}匹配至少出现n次{n,m}匹配了出现n次到m次。n,m必须是大于0小于255的。
\{n,m} 就像{n,m}一样的,但是只是在括号之前加了一个反斜扛.
\(\) 把在\(和\)之间包含着的模式存放在一个特殊的存储空间中.最多9个模式可以被存放在一行中。被子模式匹配的文本可以通过\1到\9进行“回放”
\n 回放由\(和\)括起来的第n个子模式。n是从1到9的数字。以最左位置为1开始.
\<\> 匹配以\<开始的或以\>结束的词中的字符
+ 匹配一个或多个前述的正则表达式的实例
? 匹配零个或一个前述的正则表达式的实例
| 匹配在之前或之后指定的正则表达式。
() 匹配一个括起来的一组正则表达式
很多unix系统允许在方括号括起来的一组字符里使用POSIX字符集.就是指用[:和:]括起来的内容。例如[:alnum:]就匹配一个文字数字的字符
字符集类 匹配的字符
alnum 文字数字字符
alpha 字母字符
blank 空格或TAB
cntrl 控制字符
digit 十进制数字
graph 非空格字符
lower 小写字符
print 可打印的字符
space 空格字符
upper 大写字符
xdigit 十六进制数字
1.3.2.2 替换模式
下表中的字符只在替换模式中有特殊的含义:
字符 模式
\ 关闭之前字符的特殊含义
\n 存储用\(和\)保存的n个模式匹配得到的文本。n是从1到9的数字,1是从左端开始的位置
& 重用搜索模式匹配的文本以做为替换模式
~ 重用之前的替换模式为当前的替换模式。只能为在替换模式中的唯一一个字符(ex和vi)
% 重用之前的替换模式为当前的替换模式。只能为在替换模式中的唯一一个字符(ed)
\u 把当前替换模式的第一个字符转为大写
\U 把整个替换模式转为大写。
\l 把当前替换模式的第一个字符转为小写
\L 把整个替换模式转为小写
\E 关闭之前的\U或\L
\e 关闭之前的\u或\l
1.3.3 元字符,以unix程序来列举
一些元字符对于一个程序来说是有效的,但是对于另一个也可能是无效的。在哪个unix程序中有效的我们在下表中将标出.(这个表对于SVR4和Solaris以及大多数unix系统都是正确的,但是对于你的系统来说并不一定总是执行有效的)用"P"标记的项表示是POSIX标准指定的。
符号 ed ex vi sed awk grep egrep 动作
. Y Y Y Y Y Y Y 匹配任意一个字符
* Y Y Y Y Y Y Y 匹配0个或多个之前的字符
^ Y Y Y Y Y Y Y 匹配一行或一个字符串的开始部分
$ 匹配一行或一个字符串的结束部分
\ Y Y Y Y Y Y Y 不匹配之后的字符
[] Y Y Y Y Y Y Y 从一个字符集中匹配一个字符
\( \) Y Y Y Y Y 为以后的回放来存放模式
\n Y Y Y Y Y 重放之前存储的某个模式
{ } YP YP 匹配一个范围的实例
\{ \} Y Y Y 匹配一个范围的实例
\< \> Y Y Y 在一个词的词首或词尾匹配
+ Y Y 匹配之前的一个或多个字符
? Y Y 匹配之前的零个或一个字符
| Y Y 隔离匹配的选择
() Y Y 用于匹配的一组表达式
注意在ed,ex,vi,sed中,你可以指定搜索模式(在左)也可以指定替换模式(在右).在表中列出的元字符只在搜索模式中有效.
在ed,ex,vi和sed中,以下的元字符只在替换模式中有效:
符号 ex vi sed ed 动作
\ Y Y Y Y 不匹配后一个字符
\n Y Y Y Y 匹配在\(和\)中存储的模式
& Y Y Y Y 匹配搜索模式中的文本
~ Y Y 重用之前的替换模式
% Y 重用之前的替换模式
\u\U Y Y 把字符变为大写
\l\L Y Y 把字符变成小写
\E Y Y 关闭\U或\L
\e Y Y 关闭\u或\l
1.3.4 搜索中的实例
当与grep或egrep同时使用的时候,正则表达式应该用引号引起来.(如果模式中包含$,则你必须要使用单引号)当与ed,ex,sed和awk使用正则表达式的时候,它们总要由/包围起来(awk除外),任何分界符都有效。以下是几个模式的实例:
模式 匹配的结果
bag bag字符串
^bag 以bag开始的一行
bag$ 以bag结束的一行
^bag$ 只包含bag的一行
[Bb]ag Bag或bag
b[aeiou]g 第二个字符是一个字音字母
b[^aeiou]g 第二个字母是一个辅音字母
b.g 第二个字母是任意字母
^...$ 任何只包含三个字符的一行
^\. 任何以一个"."开始的一行
^\.[a-z][a-z] 同前一个模式,之后的两个字符为小写
./[a-z]\{2\} 与之前的模式相同,仅在ed,grep和sed中使用
^[^.] 任何一个不以"."开始的一行.
bugs* bug,bugs,bugss等等
"word" 用引号引起来的词
"*word" 一个词,有或没有引号引着
[A-Z][A-Z]* 一个或多个大写字母
[A-Z]+ 同上,只有在egrep和awk使用
[[:upper:]]+ 同上,POSIX下的egrep或awk使用
[A-Z].* 一个大写的字母,后接以0个或多个字符
[A-Z]* 0个或多个大写字符
[a-zA-Z] 任意一个字母,不管是大写还是小写
[^0-9A-Za-z] 任何的一个符号或空格(不是字母或数字)
[^[:alnum:]] 同上,用POSIX的字符集
egrep或awk模式 匹配的结果
[567] 5,6,7中的一个数字
five|six|seven 一个为five或six或seven的词
80[2-4]?86 8086,80286,80386或80486
80[2-4]?86|Pentium 8086,80286,80386或80486或Pentium
compan(y|ies) company或companies
ex或vi的模式 匹配的结果
\<the 像theater,there或the这样的词
the\> 像breathe,seathe或the这样的词
\<the\> the
ed, sed或grep模式 匹配的结果
0\{5,\} 在一行中有五个以上的0
[0-9]\{3\}-[0-9]\{2\}-[0-9]\{4\}nnn-nn-nnnn格式的数字
\(why\).*\1 出现两次why的一行
\([[:alpha:]_][[:alnum:]_.]*\) = \1; C++赋值语句
1.3.4.1 搜索并替换的例子
以下的例子说明了sed或ex中的元字符。注意ex命令以冒号开始。
标的时候空格记为S,TAB记为T
命令 结果
s/.*/( & )/ 重做整行,但是加入圆括号
s/.*/mv & &.old/ 变换mv命令一个词列表(每行一词)
/^$/d 删除空行
:g/^$/d 在ex中的上面命令
/^[ST]*$/d 删除空行,和仅包含空格和[]的行
:g/^[ST]*$/d 同上,在ex中使用
s/SS*/S/g 把一个或多个空格变为一个
:%s/SS*/S/g 同上,在ex中使用
:s/[0-9]/Item &:/ 把一个数字变成一个当前行的项标
:s 在第一次出现的时候重复替换
:& 同上
:sg 同上,但是是对于任意一次出现的时候
:&g 同上
:%&g 在所有的行上重复替换
:.,$s/Fortran/\U&/g 从当前行到最后一行,把词换为大写
:%s/.*/\L&/ 整个文件变为小写
:s/\<./\u&/g 对于当前行的所有的词的首字符转为大写
:%s/yes/No/g 在全文中把词替换为No
:%s/Yes/~/g 在全文把一个不同的词替换为No
最后,这里有一些sed的替换词的例子.一个简单的替换两个字的例子如下:
s/die或do/do或die/
真正的技巧是对替换的变量模式使用缓冲。例如,用缓冲进行替换:
s/\([Dd]ie\) or \([Dd]o\)/\2 or \1/