用正则表达式进行搜索
本节讨论初级管理(LPIC-1)考试 101 的主题 1.103.7 的内容。这个主题的权值是 3。
在本节中,学习以下主题:
- 正则表达式
- 使用正则表达式搜索文件和文件系统
- 通过 sed 使用正则表达式
正则表达式
正则表达式来源于计算机语言理论。大多数计算机科学系的学生都知道,可以用正则表达式表达的语言也就是有穷自动机可以接受的语言。本节讨论的正则表达式是一种表达复杂规则的方式,所以与您在计算机科学课上学到的正则表达式 不是 一回事儿,尽管它们之间确实有继承关系。
正则表达式(也称为 “regex” 或 “regexp”)是一种描述文本字符串(即模式)的方式,它使程序能够针对任意文本字符串匹配 模式,这就提供了非常强大的搜索能力。grep(表示 generalized regular expression processor,通用正则表达式处理程序)是任何 Linux 或 UNIX 程序员或管理员的标准工具。它允许使用正则表达式搜索文件或命令输出。在 文本流和过滤器 一节中,我们介绍了 sed(stream editor,流编辑器),这是另一个大量使用正则表达式在文件或文本流中搜索并替换文本的标准工具。本节帮助您更好地理解 grep 和 sed 所使用的正则表达式。另一个大量使用正则表达式的程序是 awk,这个程序属于 LPIC-2 认证考试 201 的范围。
关于正则表达式和计算机语言理论有许多书籍。请参见 参考资料 中的一些学习建议。
在学习正则表达式时,您会发现正则表达式语法与 通配符和 globbing 一节讨论的通配符(即 globbing)语法之间存在相似性。但是,这种相似性只是表面的,这两者没有内在联系。
基本成分
大多数 Linux 系统上的 GNU grep 使用两种形式的正则表达式语法:基本的 和扩展的。对于 GNU grep,这两种形式在功能上没有区别。本节描述基本语法,以及它与扩展语法之间的差异。
正则表达式由字符 和操作符 以及元字符 组成。大多数字符匹配本身,大多数元字符必须使用反斜线(\)进行转义。基本操作如下:
串联
串联两个正则表达式创建一个更长的表达式。例如,正则表达式 a 将匹配字符串 abcdcba 两次(第一个和最后一个 a),正则表达式 b 也是这样。但是,ab 只匹配 abcdcba,ba 只匹配 abcdcba。
重复
Kleene *(即重复操作符)匹配前面正则表达式的零次或更多次出现。所以表达式 a*b 匹配有任意个 a 并以 b 结束的字符串,包括 b 本身。Kleene * 不必进行转义,所以要想在表达式中匹配字面意义的星号(*),就必须对星号进行转义。
选择
选择操作符(|)匹配前面或后面的表达式。在基本语法中必须对它进行转义。例如,表达式 a*\|b*c 匹配有任意个 a 或任意个 b(但是不能同时有)并以一个 c 结束的字符串。同样,单一字符 c 也是匹配的。
常常需要给正则表达式加上引号,以免 shell 将它展开。
我们以前面在 lpi103 目录中创建的文本文件为例。请研究清单 107 中的简单示例。注意,grep 的参数是一个正则表达式(必须有)和要搜索的零个或多个文件的列表。如果没有给出文件,grep 就会搜索 stdin,这使它成为可以在管道中使用的过滤器。如果没有匹配的行,grep 就没有输出,但是可以检查它的退出码。
xU7Linux联盟
清单 107. 简单的正则表达式
[ian@echidna lpi103]$ grep p text1
1 apple
2 pear
[ian@echidna lpi103]$ grep pea text1
2 pear
[ian@echidna lpi103]$ grep "p*" text1
1 apple
2 pear
3 banana
[ian@echidna lpi103]$ grep "pp*" text1
1 apple
2 pear
[ian@echidna lpi103]$ grep "x" text1
[ian@echidna lpi103]$ grep "x*" text1
1 apple
2 pear
3 banana
[ian@echidna lpi103]$ cat text1 | grep "l\|n"
1 apple
3 banana
[ian@echidna lpi103]$ echo -e "find an\n* here" | grep "\*"
* here
|
从这些示例中可以看到,有时候可能会得到令人吃惊的结果,尤其是在使用重复的时候。您可能期望 p* 或至少 pp* 匹配多个 p 字符,但是 p*(和 x*)匹配文件中的每一行,因为 * 操作符匹配前面正则表达式的 零次 或更多次出现。
便捷方式
既然已经有了正则表达式的基本成分,我们来看几种便捷方式。
+
+ 操作符与 * 操作符相似,只是它匹配前面正则表达式的 一次 或更多次出现。对于基本表达式,必须对它进行转义。
?
? 表示前面的表达式是可选的,所以它表示零次或一次出现。这与 globbing 中使用的 ? 不一样。
.
.(点)是一个表示任何字符的元字符。最常用的模式之一是 .*,它匹配包含任何字符(或根本没有字符)的任意长度的字符串。当然,这会用作一个长表达式的一部分。请比较单个点与 globbing 中使用的 ?,以及 .* 与 globbing 中使用的 *。
xU7Linux联盟
清单 108. 更多的正则表达式
[ian@echidna lpi103]$ grep "pp\+" text1 # at least to p's
1 apple
[ian@echidna lpi103]$ grep "pl\?e" text1
1 apple
2 pear
[ian@echidna lpi103]$ grep "pl\?e" text1 # pe with optional l between
1 apple
2 pear
[ian@echidna lpi103]$ grep "p.*r" text1 # p, some string then r
2 pear
[ian@echidna lpi103]$ xU7Linux联盟 grep "a.." text1 # a followed by two other letters
1 apple
3 banana
|
匹配行的开头或结尾
^(脱字符)匹配行的开头,$(美圆符号)匹配行的结尾。所以 ^..b 匹配行首的任意两个字符,后面跟着一个 b;ar$ 匹配任何以 ar 结尾的行。正则表达式 ^$ 匹配空行。
更复杂的表达式
到目前为止,我们见过了对单个字符应用重复。如果希望搜索一个多字符字符串的一次或多次出现,比如在 banana 中两次出现的 an,那么使用圆括号,这在基本语法中必须进行转义。与此相似,可能希望搜索几个字符,而不使用 . 那样宽泛的搜索或者很长的选择。可以将要搜索的字符放在方括号([])中,对于基本语法,这不需要转义。方括号中的表达式组成了一个字符类。除了后面讨论的几种例外情况之外,使用方括号还可以避免对特殊字符(. 和 *)进行转义。
xU7Linux联盟
清单 109. 圆括号和字符类
[ian@echidna lpi103]$ grep "\(an\)\+" text1 # find at least 1 an
3 banana
[ian@echidna lpi103]$ grep "an\(an\)\+" text1 # find at least 2 an's
3 banana
[ian@echidna lpi103]$ grep "[3p]" text1 # find p or 3
1 apple
2 pear
3 banana
[ian@echidna lpi103]$ xU7Linux联盟 echo -e "find an\n* here\nsomewhere." | grep "[.*]"
* here
somewhere.
|
对于字符类,有另外几种有意思的情况。
范围表达式
范围表达式是由 -(连字符)分隔的两个字符,比如 0-9 表示数字,0-9a-fA-F 表示十六进制数字。注意,范围依赖于系统的地区设置。
命名的类
几个命名的类提供了常用类的便捷方式。命名的类以 [: 开头,以 :] 结尾。包括:
[:alnum:]
字母数字字符
[:blank:]
空白和制表符字符
[:digit:]
数字 0 到 9(相当于 0-9)
[:upper:] 和 [:lower:]
分别表示大写和小写字母
^(取反)
如果作为方括号中的第一个字符使用,^(脱字符)就对其余字符的意义取反,所以只匹配 不在这个类 中的字符。
由于有特殊意义,如果希望在字符类中匹配字面意义的 -(连字符),就必须将它放在最前面或最后面。如果希望匹配字面意义的 ^(脱字符),那么必须不放在最前面。](右方括号)结束字符类,除非它出现在最前面。
字符类是正则表达式和 globbing 相似的一个方面,但是取反符号不一样(^ 和 !)。清单 110 给出一些字符类示例。
xU7Linux联盟
清单 110. 更多的字符类
[ian@echidna lpi103]$ # Match on range 3 through 7
[ian@echidna lpi103]$ echo -e "123\n456\n789\n0" | grep "[3-7]"
123
456
789
[ian@echidna lpi103]$ # xU7Linux联盟 Find digit followed by no n or r till end of line
[ian@echidna lpi103]$ grep "[[:digit:]][^nr]*$" text1
1 apple
|
通过 sed 使用正则表达式
前面对 sed 的简短介绍中提到过,sed 要使用正则表达式。正则表达式可以在地址表达式和替换表达式中使用。所以表达式 /abc/s/xyz/XYZ/g 意味着 只 对包含 abc 的行应用替换命令,将每个 xyz 替换为 XYZ。清单 111 给出应用于 text1 文件的两个示例,另一个示例将点(.)前面的最后一个单词替换为字符串 LAST WORD。注意,字符串 First 没有被修改,因为它前面没有空格。 xU7Linux联盟
Linux联盟收集整理 ,转贴请标明原始链接,如有任何疑问欢迎来本站Linux论坛讨论