正则表达式
本页总结了一些关于正则表达式的详细信息。它并非旨在作为任何正则表达式语言的教程,而是一个技术总结和“陷阱”(工具的不同实现可能以不同或意外方式运行的地方)列表。

Glob 模式
这些用于 shell 扩展和 case
表达式中的模式匹配。
glob*
gl?bbing
[a-z]
和[!-aeiou]
[!-aeiou]
在某些 shell(包括 BusyBox ash
)中也可以表示为 [^-aeiou]
,但 [!...]
格式更具可移植性。
- 如果模式包含无效的括号表达式或与任何现有文件名或路径名不匹配,则模式字符串将被字面解释。
- 前导句点只能字面匹配,不能通过
[!a]
、?
、*
、[%-0]
或[[:punct:]]
匹配。在 BusyBox ash 中,它与[.a]
不匹配;其他 shell 实现可能有所不同。 /
只能字面匹配,并且解析优先级高于[...]
,因此a[b/c]d
仅匹配目录 a[b 中的文件 c]d。- 给定模式
/foo/bar/x*/bam
,目录 / 和 foo 需要搜索权限,目录 bar 需要搜索和读取权限,每个 x* 目录都需要搜索权限。 - 如果
set -f
/set -o noglob
,则禁用 glob 扩展。待办事项: 哪些上下文会自动抑制 glob 扩展?
问: 如何构造一个 shell glob 模式,以匹配除 "." 和 ".." 之外的所有文件?(来自 Unix FAQ 2.11)
模式 | 匹配 . 吗? | 匹配 .. 吗? | 匹配 .a 吗? | 匹配 .ab 吗? | 匹配 ..pdq 吗? | 匹配 xyz 吗? |
---|---|---|---|---|---|---|
*
|
否 | 否 | 否 | 否 | 否 | 是 |
.*
|
是 | 是 | 是 | 是 | 是 | 否 |
.[!.]*
|
否 | 否 | 是 | 是 | 否 | 否 |
.??*
|
否 | 否 | 否 | 是 | 是 | 否 |
因此,要匹配最右边四列中的所有列,但不匹配最左边两列中的任何列,你需要组合三个 glob 模式:* .[!.]* .??*
如果你没有任何长度为 2 的文件名(如 .a),则可以使用更简单的模式:* .??*
POSIX 正则表达式
POSIX 定义了两类正则表达式语言:基本正则表达式 (BREs) 和 扩展正则表达式 (EREs)。第一类在历史上由 grep
、sed
和 ed
等实用程序实现。第二类由 egrep
(grep -E
)、awk
、lex
和 emacs
等工具实现。EREs 通常也可用作 sed
的一个选项;有时使用 sed -r
表示,有时使用 sed -E
表示。(BusyBox sed
使用前者。)
实际上,这些正则表达式语言的大多数实现都超出了规范,例如包括 Gnu 扩展(如 \w
),或包括历史上仅可用(且仅为另一种正则表达式语言指定)的功能:因此,BREs 的大多数实现也将支持 \+
和 \|
,而 EREs 的许多实现也将支持反向引用(如 \1
)。此外,awk
EREs 遵守的规则与其他 EREs 略有不同。
这些引擎的朴素实现之间的一个区别是 NFAs 更“渴望”。两种类型的引擎都将返回源文本中几个可能匹配项中最左边的匹配项;但 NFA 的更大渴望会体现在它用于匹配的几种替代模式中的哪一种。对于任何可以处理替代模式的正则表达式引擎,如果引擎是 DFA,则 printf "NFA, no I mean DFA" | regex_match "/NFA|NFA, no I mean DFA/"
将匹配整个源文本;但如果引擎是(朴素的)NFA,则只会匹配“NFA”,使用左侧模式。
然而,如今这种情况变得复杂,因为 POSIX 规定必须匹配源文本中最长的最左边延伸:因此,符合 POSIX 标准的 grep
(它接受模式交替作为扩展)也必须匹配“NFA, no I mean DFA”,就像 egrep
一样。
POSIX 为 BRE 和 ERE 规定
- 匹配应该是文本中最长的最左匹配
- 子模式贪婪匹配(匹配空字符串 "" 优于不匹配)
- 空字符 (\0) 在文本和模式中都不允许
在下面,我将 BusyBox 工具(grep
、egrep
、带有和不带有 -r
的 sed
以及 awk
)与 Gnu 核心工具以及 FreeBSD 9 基础系统中的这些工具的版本进行了比较。我将 Gnu 的 awk
实现称为 “gawk”(Gnu 本身也是如此);我将 FreeBSD 的 awk
实现称为 “nawk”(尽管严格来说,这只是 nawk 的几种实现之一)。
BREs
![]()
|
EREs
![]()
|
Gnu 扩展
这些存在于我测试的所有 [e]greps 中,以及除 nawk
(和 gawk --traditional
)之外的所有 seds 和 awks 中。它们仅在括号表达式之外被特殊处理,即使在 awks 中也是如此,后者仍然在那里特殊处理 \t
等。
\w
和\W
用于[[:alnum:]_]
和[^[:alnum:]_]
\s
和\S
用于[[:space:]]
和[^[:space:]]
,匹配以下任何一项:空格 制表符 \n \r \v \f- (BusyBox 工具和某些版本的
gawk
缺少。)
- (BusyBox 工具和某些版本的
\b \B \<
和\>
,在单词边界处的零宽度匹配(\B
为非单词边界)- (在 awks 中,
\b
而是指 “\x08”;\y
在gawk
中替代\b
,BusyBox awk 中没有替代\b
的。) - FreeBSD 的
[e]grep -o \b...
和[e]grep -o \<...
目前存在错误;BusyBox 的 sed 和 awk 在单词开头使用\< \b \B
时存在错误。
- (在 awks 中,
\` \'
缓冲区开头和缓冲区结尾锚点(此处未调查的某些正则表达式引擎使用\A \Z \z
代替)- (在 awks 中,
^
和$
已经具有此行为,即使针对包含换行符的源文本也是如此。) - FreeBSD 的
[e]grep
目前错误地将这些与行首和行尾而不是缓冲区开头和缓冲区结尾匹配。此外,FreeBSD 的grep -o '\`...'
在某些方面存在错误,而grep -o '...'\'
则没有。BusyBox sed 和 awk 在使用\`
时也存在错误,而使用\'
时则没有。所有这些错误都已报告。
- (在 awks 中,
C 转义符
\n \t \r \x09 \f \v \a \c
- 这些由 awks 特殊处理(尽管
nawk
仅支持到\f
),即使在括号内也是如此。 - 它们也由 Gnu 的 sed 特殊处理。BusyBox 的 sed 支持
\n \t \r
;FreeBSD 的 sed 支持\n
。其他转义符不受这些 seds 支持,我测试的任何 grep 都不支持。
这些转义符也可能在你的 shell 的 $'...'
构造中被特殊处理;\OOO
(最多 3 位八进制数字)、\uXXXX
(4 位十六进制数字)、\e \E \b \'
也可能被特殊处理。Awk 引擎也处理了这些后来的形式中的一些。如上文所述,最后两个被某些正则表达式引擎以不同的方式处理。
注意
- 所有这些正则表达式引擎都将
\d
视为字面量 “d”,而不是[[:digit:]]
。
- BusyBox sed 将仅匹配一次 ""(空字符串);如果
/g
修饰符打开,则其他将匹配多次。
- Grep 引擎会将模式中的换行符视为等同于
\|
;sed 和 awk 引擎将拒绝并报错。
- FreeBSD 的 sed 将强制存在终端
\n
,即使输入中不存在也是如此。一些其他 FreeBSD 工具(如cut
)也会这样做;另一些(如tr
)则不会。在 Gnu 的 sed 中,命令q
也强制终端\n
。
- BusyBox 的
grep -oz
在每个结果后添加 “\0”(空字符)后缀;其他 greps 添加\n
后缀。
- 非贪婪量词
pat?? pat*? pat+? pat\{m,n\}?
在 POSIX 规范中未提供,此处讨论的任何符合 POSIX 标准的工具也未提供。
- FreeBSD grep 和 egrep 在位置 0 匹配空字符串:
printf 'cba' | egrep -o '[ba]*'
。我检查的其他任何 [e]grep 实现(例如 BusyBox 或 Gnu 的)都不会这样做。
Lua 中的正则表达式
字符串转义
在 Lua 中,正则表达式模式始终以字符串形式提供,因此将支持字符串上的所有常规转义符
\\ \" \' \a for bell, \x07 \b for backspace, \x08 \t for \x09 \n for \x0a \v for \x0b \f for \x0c \r for \x0d
Lua 字符串也接受 ddd
用于十进制数字 d。(注意:不是八进制数字。)从 Lua 5.2 开始,也接受用于十六进制数字的 \xhh
。
字符串可以写在匹配的单引号或双引号内。它们也可以写在
[[constructs like this]] [=[ or [[like]] \this]=]
在这些构造中,转义序列(如 \t
)不会被扩展。它和嵌入的 [[like]]
都被字面解释。此外,当字符串的第一个字符是换行符时,它会被忽略。
正则表达式引擎
基本的 Lua 正则表达式引擎比 Posix 或 PCRE 风格的语言更有限,但仍然非常强大。事实上,与更熟悉的引擎相比,Lua 引擎更容易完成某些事情。如果基本的 Lua 引擎对于你的目的而言仍然太有限,你应该考虑 LPEG 或 Lrexlib 库。前者在 Lua 社区中更强大且被广泛使用;后者与更熟悉的正则表达式引擎库和语言接口。
正则表达式特殊字符
以下序列对于基本的 Lua 正则表达式引擎具有特殊含义。
- 字符类
-
. 匹配任何字符
%z 在 Lua 5.1 中,正则表达式引擎不会读取模式字符串中嵌入的 \0 之后的内容,因此提供了此特殊序列来匹配源文本中的 \0(并允许模式字符串继续)。在 Lua 5.2 中,现在可以直接使用嵌入的 \0。使用默认编译设置,%z 仍然被支持,但已弃用。
%a 和 %A 类似于 POSIX [[:alpha:]] 和 [^[:alpha:]] %l 和 %L 类似于 POSIX [[:lower:]] 和 [^[:lower:]] %u 和 %U 类似于 POSIX [[:upper:]] 和 [^[:upper:]]
%w 和 %W 类似于 POSIX [[:alnum:]] 和 [^[:alnum:]]。(请注意,与 Gnu 正则表达式扩展\w
不同,Lua 中的模式%w
和 POSIX 中的[[:alnum:]]
不匹配下划线。)%d 和 %D 类似于 POSIX [[:digit:]] 和 [^[:digit:]] %x 和 %X 类似于 POSIX [[:xdigit:]] 和 [^[:xdigit:]]
%s 和 %S 类似于 POSIX [[:space:]] 和 [^[:space:]]。(%s 和 POSIX [[:space:]] 也匹配垂直空格(\n \r \f \v,而 POSIX [[:blank:]] 仅匹配 \x20 和制表符。)%p 和 %P 类似于 POSIX [[:punct:]] 和 [^[:punct:]],排除空格、字母数字和控制字符 %c 和 %C 类似于 POSIX [[:cntrl:]] 和 [^[:cntrl:]] %g 和 %G 类似于 POSIX [[:graph:]] 和 [^[:graph:]],所有可见字符(空格除外);仅在 Lua 5.2 中如此解释 POSIX[[:print:]]
,所有可见字符加上空格,不可直接使用。使用[%p%w ]
或[%g ]
。 - 括号表达式
- [class] 和 [^class] 可以包含以下序列
- 单个字符,如
a
或\t
- 范围,如
a-m
- 字符类特殊字符,如
%a
- 单个字符,如
- 组和反向引用
-
(pat)
将匹配 pat 的源文本捕获到一个组中。与其他正则表达式引擎不同,这些构造不能后跟量词(如*
或+
)%1
反向引用到捕获的组(对比\1
,它是并匹配 “\x01”,以及\\1
,它匹配字面字符 “\” 然后是 “1”)()
捕获匹配源中的当前位置到一个组中,而不是文本
- 量词
-
?
、*
和+
是熟悉的贪婪量词-
是*
的非贪婪变体
- 单个字符
- 正则表达式特殊字符,如
.
和%a
[class]
表达式
(pat)
- 锚点 ^ $
- 与 POSIX BREs 中一样,当不在锚定位置时(如在模式
ab^cd
中),这些被视为字面字符 - 字面字符
%c
这是一个字面量 c,对于任意字符 c。它取消了字符( ) . % + - * ? [ ] ^ $
的特殊含义
交替(在其他正则表达式语言中使用 |
表示)在 Lua 中不可用;它只能使用 [class]
构造来近似。
Lua 拥有而其他引擎缺乏的两个巧妙的原语是
%b()
平衡的(...)
内部(包括)文本;可以使用其他字符代替(
和)
。%f[class]
(源的开头或) 不匹配 class 的文本与 (源的结尾或) 匹配 class 的文本之间的零宽度“边界”。这是 Gnu 正则表达式特殊字符\<
和\>
的概括。示例%f[%x] matches the source text "123-567 9ab" before positions 1, 5, and 9 %f[%X] matches the same source text before positions 4, 8, and 12.