Linux shell - regexp
正则表达式基础
理解正则表达式的第一步是弄清它到底是什么。
定义
正则表达式是一种可供 Linux 工具过滤文本的自定义模板。Linux 工具(比如 sed 或 gawk)会在读取数据时使用正则表达式对数据进行模式匹配。如果数据匹配模式,它就会被接受并进行处理。如果数据不匹配模式,它就会被弃用。
正则表达式模式使用元字符来描述数据流中的一个或多个字符。Linux 中有很多场景可以使用特殊字符来描述具体内容不确定的数据。
可以使用通配符*列出满足特定条件的文件。例如:
ls -al da*
# -rw-rw-r-- 1 roger roger 94 Jun 21 00:08 data12.txt
# -rw-rw-r-- 1 roger roger 180 Jun 19 22:37 data1.txt
# -rw-rw-r-- 1 roger roger 21 Jun 19 22:46 data3.txt
# -rw-rw-r-- 1 roger roger 79 Jun 20 22:33 data4.txt
# -rw-rw-r-- 1 roger roger 90 Jun 20 23:41 data6.txt
# -rw-rw-r-- 1 roger roger 217 Jun 20 23:20 data7.txt
da*参数会使 ls 命令只列出名称以 da 起始的文件。文件名中的 da 之后可以有任意多个字符(包括 0 个)。ls 命令会读取目录中所有文件的信息,但只显示与通配符匹配的那些文件的信息。
正则表达式的工作方式与通配符类似。正则表达式包含文本和/或特殊字符,定义了 sed 和 gawk 匹配数据时使用的模板。你可以在正则表达式中使用不同的特殊字符来定义特定的数据过滤模式。
正则表达式的类型
使用正则表达式最大的问题在于有不止一种类型的正则表达式。在 Linux 中,不同的应用程序可能使用不同类型的正则表达式。这其中包括编程语言(比如 Java、Perl 和 Python)、Linux 工具(比如 sed、gawk 和 grep)以及主流应用程序(比如 MySQL 数据库服务器和 PostgreSQL 数据库服务器)。
正则表达式是由正则表达式引擎实现的。这是一种底层软件,负责解释正则表达式并用这些模式进行文本匹配。
尽管在 Linux 世界中有很多不同的正则表达式引擎,但最流行的是以下两种。
- POSIX 基础正则表达式(basic regular expression,BRE)引擎。
- POSIX 扩展正则表达式(extended regular expression,ERE)引擎。
大多数 Linux 工具至少符合 POSIX BRE 引擎规范,能够识别该规范定义的所有模式符号。遗憾的是,有些工具(比如 sed)仅符合 BRE 引擎规范的一个子集。这是出于速度方面的考虑导致的,因为 sed 希望尽可能快地处理数据流中的文本。
POSIX、ERE 引擎多见于依赖正则表达式过滤文本的编程语言中。它为常见模式(比如数字、单词以及字母数字字符)提供了高级模式符号和特殊符号。gawk 使用 ERE 引擎来处理正则表达式。
由于正则表达式的实现方法众多,因此很难用一种简洁的描述来涵盖所有可能的正则表达式。
定义 BRE 模式
最基本的 BRE 模式是匹配数据流中的文本字符。
普通文本
下面的例子演示了在 sed 和 gawk 中用标准文本字符串过滤数据:
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 显示了该行。
第二种模式也定义了一个单词,这次是 trial。因为 echo 语句的文本字符串中未包含该单词,所以正则表达式没有匹配,sed 和 gawk 都没打印该行。
你大概已经注意到,正则表达式并不关心模式在数据流中出现的位置,也不在意模式出现了多少次。只要能匹配文本字符串中任意位置的模式,正则表达式就会将该字符串传回 Linux 工具。
这里的关键在于,将正则表达式应用于数据流文本。记住,正则表达式对匹配的模式非常挑剔,这很重要。第一条匹配原则是正则表达式区分大小写,这意味着它只会匹配大小写也相符的模式:
echo "This is a test" | sed -n '/this/p'
echo "This is a test" | sed -n '/This/p'
# This is a test
第一次尝试没能匹配成功,因为 this 的首字母在字符串中不是小写,而第二次尝试在模式中使用首字母大写形式,所以能成功匹配。
在正则表达式中,无须写出整个单词。只要定义的文本出现在数据流中,正则表达式就能够匹配:
echo "The books are expensive" | sed -n '/book/p'
# The books are expensive
尽管数据流中的文本是 books,但数据中含有正则表达式 book,因此正则表达式能匹配数据。当然,反过来就不行了:
echo "The book is expensive" | sed -n '/books/p'
由于数据流中没有能够完全匹配正则表达式的文本,因此匹配失败,sed 编辑器不会显示任何文本。
你也无须局限于在正则表达式中只使用单个文本单词,空格和数字也是可以的:
echo "This is line number 1" | sed -n '/ber 1/p'
# This is line number 1
在正则表达式中,空格和其他的字符没有什么区别:
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.
单词间有两个空格的行匹配了正则表达式模式。这是查找文本文件中空格的好办法。
特殊字符
在书写正则表达式时,你得留意。正则表达式中的一些字符具有特别的含义。如果要在匹配普通文本的模式中使用这些字符,那么结果会出乎你的意料。
正则表达式能识别的特殊字符如下所示:
.*[]^${}\+?|()
随着学习的深入,你会了解到这些特殊字符在正则表达式中有何用途。不过现在只需记住不能在匹配普通文本的模式中单独使用这些字符即可。
如果要将某个特殊字符视为普通字符,则必须将其转义。在转义特殊字符时,需要在它前面加上另一个特殊字符来告诉正则表达式引擎,应该将接下来的字符视为普通字符。这个特殊字符就是反斜线(\)。
如果要查找文本中的美元符号,则需要在它前面加个反斜线:
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 能正确解释正则表达式模式了,一切正常。