Shell 使用正则表达式,正则表达式是基于模式匹配的文本处理技术的关键所在。想要有效地运用正则表达式,就必须对其有一个基本的理解。
会用ls
的用户应该都熟悉通配符模式。通配符可以运用在很多场景中,但是对于文本处理而言,功能还远远不够。正则表达式允许你更精细地描述模式。[a-z0-9_]+@[a-z0-9]+\.[a-z]+.
就是一个典型的能够匹配电子邮件地址的正则表达式。看起来有点怪异是吧?别担心,跟着本章学习,你就会发现它其实很简单。
实战演练
正则表达式是由字面文本和具有特殊意义的符号组成的。我们可以根据需要,构造出适合的正则表达式来匹配任何文本。正则表达式是很多工具所支持的基本功能。本节将为你讲解正则表达式,但不会介绍其所涉及的Linux/Unix工具。这些工具留待随后的其他攻略中再叙。
下面先来学习正则表达式的规则。
- 位置标记
位置标记锚点(position marker anchor)是标识字符串位置的正则表达式。默认情况下,正则表达式所匹配的字符可以出现在字符串中任何位置,见下表。
正则表达式 | 描述 | 示例 |
---|---|---|
^ |
指定了匹配正则表达式的文本必须起始于字符串的首部 | ^tux 能够匹配以tux 起始的行 |
$ |
指定了匹配正则表达式的文本必须结束于目标字符串的尾部 | tux$ 能够匹配以tux 结尾的行 |
- 标识符
标识符是正则表达式的基础组成部分。它定义了那些为了匹配正则表达式,必须存在(或不存在)的字符,见下表。
正则表达式 | 描述 | 示例 |
---|---|---|
A 字符 |
正则表达式必须匹配该字符 | A能够匹配字符A |
. |
匹配任意一个字符 | Hack. 能够匹配Hackl 和Hacki ,但是不能匹配Hackl2 或Hackil ,它只能匹配单个字符 |
[] |
匹配中括号内的任意一个字符。中括号内可以是一个字符组或字符范围 | coo[kl] 能够匹配cook 或cool ,[0-9] 匹配任意单个数字 |
[^] |
匹配不在中括号内的任意一个字符。中括号内可以是一个字符组或字符范围 | 9[^01] 能够匹配92 和93 ,但是不匹配91 和90 ;A[^0-9] 匹配A 以及随后除数字外的任意单个字符 |
- 数量修饰符
一个标识符可以出现一次、多次或是不出现。数量修饰符定义了模式可以出现的次数,见下表。
正则表达式 | 描述 | 示例 |
---|---|---|
? |
匹配之前的项1次或0次 | colou?r 能够匹配color 或colour ,但是不能匹配colouur |
+ |
匹配之前的项1次或多次 | Rollno-9+ 能够匹配Rollno-99 和Rollno-9 ,但是不能匹配Rollno- |
\* |
匹配之前的项0次或多次 | co*l 能够匹配cl 、col 和coool |
{n} |
匹配之前的项n次 | [0-9]{3} 能够匹配任意的三位数,[0-9]{3} 可以扩展为[0-9][0-9][0-9] |
{n,} |
之前的项至少需要匹配n次 | [0-9]{2,} 能够匹配任意一个两位或更多位的数字 |
{n,m} |
之前的项所必须匹配的最小次数和最大次数 | [0-9]{2,5} 能够匹配两位数到五位数之间的任意一个数字 |
- 其他
还有其他一些特殊字符可以调整正则表达式的匹配方式,见下表。
正则表达式 | 描述 | 示例 |
---|---|---|
() |
将括号中的内容视为一个整体 | ma(tri)?x 能够匹配max 或matrix |
\| |
指定了一种选择结构,可以匹配 \| 两边的任意一项 |
Oct (1st \| 2nd) 能够匹配Oct 1st 或Oct 2nd |
\ |
转义字符可以转义之前介绍的特殊字符 | a\.b 能够匹配a.b ,但不能匹配ajb 。因为\ 忽略了. 的特殊意义 |
正则表达式的更多细节请参考:http://www.linuxforu.com/2011/04/sed-explained-part-1/。
- 补充内容
来看几个正则表达式的例子。能够匹配任意单词的正则表达式:
( +[a-zA-Z]+ +)
开头的+
表示需要匹配一个或多个空格。字符组[a-zA-Z]
用于匹配所有的大小写字母。随后的+
表示至少要匹配一个字母,多者不限。最后的+
表示需要匹配一个或多个空格来终结单词。
这个正则表达式无法匹配句子末尾的单词。要想匹配句尾或是逗号前的单词,需要将正则表达式改写为:
( +[a-zA-Z]+[?,.]? +)
[?,.]?
表示仅需要匹配问号、逗号或点号中的一个。匹配IP地址很容易。我们知道IP地址是由点号分隔的4组三位数字。[0-9]
表示匹配数字。{1,3}
表示至少一位数字,至多三位数字:
[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}
或者也可以使用[[:digit:]]
表示数字:
[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}
我们知道IP地址是由点号分隔的4个整数(每一个整数的取值范围从0到255),例如192.168.0.2。
这个正则表达式可以匹配文本中的IP地址,但是它并不检查地址的合法性。例如,形如123.300.1.1的IP地址可以被正则表达式匹配,但这却是一个非法的IP地址。
工作原理
正则表达式由复杂的状态机解析,尝试在目标文本中找出最佳匹配。文本可以是管道的输出、文件,甚至是在命令行中输入的字符串。正则表达式的实现方法不止一种,其实现引擎通常会选择最长的匹配。
例如,对于字符串this is a test
和正则表达式s.*s
,匹配的内容是s is a tes
,而非s is
。正则表达式的更多细节请参考:http://www.linuxforu.com/2011/04/sed-explained-part-1/。
补充内容
前面的多个表格描述了正则表达式中特殊字符的含义。
- 处理特殊字符
正则表达式用$
、^
、.
、*
、+
、{
以及}
等作为特殊字符。但是如果我们希望将这些字符作为普通字符使用,应该怎么做呢?来看一个正则表达式:a.txt
。
该正则表达式能够匹配字符a
,然后是任意字符(由.
负责匹配),接着是字符串txt
。但是我们希望.
能够匹配字面意义上的.
,而非任意字符。因此需要在.
之前加上一个反斜线\
(这叫作“字符转义”)。这表明正则表达式希望匹配的是字面含义,而不是它所代表的特殊含义。因此,最终的正则表达式就变成了a\.txt
。
- 可视化正则表达式
正则表达式不容易理解。幸好有一些将正则表达式进行可视化的工具。你可以在页面http://www.regexper.com中输入正则表达式,然后创建出一副图示来帮助你理解。下图就是一个简单的正则表达式的可视化截图。