正则表达式
-
正则表达式(regexp)是描述一组字符串的一种方法。因为正则表达式是
awk
编程的基本组成部分,所以它们的格式和用法应该单独一章讨论。 -
用斜杠(“/”)括起来的正则表达式是一种awk模式,它匹配文本属于该集的每个输入记录。最简单的正则表达式是字母、数字或两者的序列。这样的regexp匹配包含该序列的任何字符串。因此,正则表达式
'foo'
匹配任何包含
'foo'
的字符串。因此,模式
/foo/
匹配任何包含记录中任意三个相邻字符
'foo'
的输入记录。其他类型的正则表达式允许您指定更复杂的字符串类。
1、如何使用正则表达式
-
通过将正则表达式括在斜杠中,可以将其用作模式。然后针对每个记录的整个文本测试正则表达式。(通常,它只需要匹配文本的某些部分就可以成功。)例如,在字符串
'li'
出现在每个记录的任何位置时,打印记录的第二个字段:$ awk '/li/ { print $2 }' mail-list -| 555-5553 -| 555-0542 -| 555-6699 -| 555-3430
-
正则表达式也可以用于匹配表达式。这些表达式允许您指定要匹配的字符串;它不必是整个当前输入记录。两个运算符
~
和
!~
执行正则表达式比较。使用这些运算符的表达式可以用作模式,也可以用在
if
、
while
、
for
和
do
语句中。(参见
Control Statements in Actions
一节。)例如,如果表达式
exp
(作为字符串)与
'regexp'
匹配,则以下为真:exp ~ /regexp/
-
此示例匹配或选择第一个字段中某处大写字母
'J'
的所有输入记录:$ awk '$1 ~ /J/' inventory-shipped -| Jan 13 25 15 115 -| Jun 31 42 75 492 -| Jul 24 34 67 436 -| Jan 21 36 64 620
-
下面例子同上效果一样:
awk '{ if ($1 ~ /J/) print }' inventory-shipped
-
如果表达式
exp
(作为字符串)与
'regexp'
不匹配,则下一个示例为
true
:exp !~ /regexp/
-
以下示例匹配或选择第一个字段不包含大写字母
'J'
的所有输入记录:$ awk '$1 !~ /J/' inventory-shipped -| Feb 15 32 24 226 -| Mar 15 24 34 228 -| Apr 31 52 63 420 -| May 16 34 29 208 …
-
当
'regexp'
用斜杠括起来时,比如
/foo/
,我们称它为
regexp
常量,就像
5.27
是数字常量,
'foo'
是字符串常量一样。
2、转义序列
-
某些字符不能包含在字符串常量(
'foo'
)或正则表达式常量(
/foo/
)中。相反,它们应该用转义序列来表示,转义序列是以反斜杠(
'\'
)开头的字符序列。转义序列的一种用法是在字符串常量中包含双引号字符。由于纯双引号以字符串结尾,因此必须使用
'\'
,将实际的双引号字符表示为字符串的一部分。例如:$ awk 'BEGIN { print "He said \"hi!\" to her." }' -| He said "hi!" to her.
反斜杠字符本身是另一个通常不能包含的字符;必须写入
'\\'
才能在字符串或regexp中放入一个反斜杠。因此,内容为两个字符
'"'
和
'\'
的字符串必须写为
"\”\\"
。 -
下面的列表显示了
awk
中使用的所有转义序列以及它们所代表的内容。除非另有说明,所有这些转义序列都适用于字符串常量和regexp常量:-
\\
:
反斜杠
'\'
。 -
\a
:
警报
字符
Ctrl-g
,
ASCII码 7(BEL)
。(这通常会发出某种声音。) -
\b
:
退格
Ctrl-h
,
ASCII code 8 (BS)
-
\f
:
Formfeed格式
,
Ctrl-l
,
ASCII code 12 (FF)
. -
\n
:
换行
,
Ctrl-j
,
ASCII code 10 (LF)
. -
\r
:
回车
,
Ctrl-m
,
ASCII code 13 (CR)
. -
\t
:
Horizontal TAB
,
Ctrl-i
,
ASCII code 9 (HT)
. -
\v
:
Vertical TAB
,
Ctrl-k
,
ASCII code 11 (VT)
. -
\nnn
:
八进制值nnn
,其中
nnn
表示
'0'
和
'7'
之间的1到3位数字。例如,ASCII ESC(转义)字符的代码是“\033”。 -
\xhh…
:
十六进制值hh
,其中
hh
表示十六进制数字序列(
'0'–'9'
,以及
'A'–'F'
或
'a'–'f'
)。
'\x'
后面最多允许有两个数字。任何其他十六进制数字都被视为简单的字母或数字。(c.e.)(POSIX awk中不允许
'\x'
转义序列。)
-
3、正则表达式运算符
可以将正则表达式与特殊字符(称为正则表达式运算符或元字符)组合,以增强正则表达式的功能和多功能性。
1、awk中的Regexp运算符
前面在
Escape Sequences
中描述的转义序列在regexp中是有效的。它们由
'\'
引入,作为处理regexp的第一步被识别并转换为相应的实数字符。
下面是元字符列表。所有不是转义序列且未在此列出的字符都代表它们自己:
-
\
: 这将抑制匹配时字符的特殊含义。例如,
'\$'
与字符
'$'
匹配。 -
^
:这与字符串的开头匹配。例如,
'^@chapter'
匹配字符串开头的
'@chapter'
,并可用于标识Texinfo source files中的章节开头。
'^'
被称为锚定,因为它锚定模式以仅在字符串的开头匹配。重要的是要认识到
'^'
与嵌入字符串中的行首(换行符
'\n'
后面的点)不匹配。以下示例中的条件是
not true
:if ("line1\nLINE 2" ~ /^L/) …
-
$
:这类似于
'^'
,但它只在字符串的末尾匹配。例如,
'p$'
匹配以
'p'
结尾的记录。
'$'
是一个锚点,与字符串中嵌入的行尾(换行符
'\n'
前的点)不匹配。以下示例中的条件是
not true
:if ("line1\nLINE 2" ~ /1$/) …
- 方法
4、有多少文本匹配?
-
考虑以下:
echo aaaabcd | awk '{ sub(/a+/, "<A>"); print }'
本例使用
sub()
函数更改输入记录。(
sub()
将与第一个参数匹配的任何文本的第一个实例替换为作为第二个参数提供的字符串;请参阅
String-Manipulation Functions
一节。)这里,regexp
/a+/
表示
一个或多个‘a’字符
,替换文本为
'<A>'
。输入包含四个
'a'
字符。
awk
(和POSIX)正则表达式总是匹配能够匹配的最左边、最长的输入字符序列。因此,在本例中,所有四个
'a'
字符都替换为
'<A>'
:$ echo aaaabcd | awk '{ sub(/a+/, "<A>"); print }' -| <A>bcd
对于简单的匹配/不匹配测试,这并不重要。但是在使用
match()
、
sub()
、
gsub()
和
gensub()
函数进行文本匹配和替换时,这一点非常重要。理解这一原则对于基于regexp的记录和字段拆分也很重要(请参阅
How Input Is Split into Records
一节,以及
Specifying How Fields Are Separated
一节)。
5、使用动态regexp
'~'
或
'!~'
运算符不必是regexp常量(即斜杠之间的字符串)。它可以是任何表达式。表达式被求值并在必要时转换为字符串;然后字符串的内容被用作
regexp
。以这种方式计算的regexp称为
动态regexp
或
computed regexp
:
BEGIN { digits_regexp = "[[:digit:]]+" }
$0 ~ digits_regexp { print }
这会将
digits_regexp
设置为描述一个或多个数字的regexp,并测试输入记录是否与此regexp匹配。
当使用
'~'
或
'!~'
运算符,请注意用斜杠括起来的regexp常量和用双引号括起来的字符串常量之间有区别。如果要使用字符串常量,则必须了解字符串本质上是扫描两次的:第一次是在awk读取程序时,第二次是在操作符左侧的字符串与右侧的模式匹配时。这适用于任何字符串值表达式(如前一示例中所示的
digits_regexp
),而不仅仅是字符串常量。