Linux技巧详解正则表达式和通配符的用法和它们的区别

49次阅读

共计 18656 个字符,预计需要花费 47 分钟才能阅读完成。

形容正则表达式、通配符的用法,以及它们之间的区别

正则表达式的标准规范文档

正则表达式可用于匹配特定模式的字符串。

在 Linux 中,能够用 man 7 regex 命令查看正则表达式的阐明,外面提到 POSIX.2 规范定义了正则表达式标准。

这外面的阐明比拟乱,内容也比拟老,倡议查看最新版的 POSIX 规范。

在网上找不到 POSIX 规范的在线版本,也能够查看 Single UNIX Specification 规范的在线版本。

Single UNIX Specification 规范和 POSIX 规范大部分内容是一样的。

2019 年最新版 Single UNIX Specification 规范的正则表达式标准在线链接是:https://pubs.opengroup.org/on…

在上面的形容中,如无特地阐明,对于正则表达式的援用内容都出自这个在线链接,并统称为 POSIX 规范。

留神:正则表达式自身是一套匹配字符串的标准,在 Linux 中没有一个独立的正则表达式命令来应用它,而是要在反对正则表达式的工具上应用

不同的工具(grep、awk、sed、find、vim,等)和不同的程序语言(Perl、Python、Java、JavaScript,等)都反对解析正则表达式,而且它们各自做了一些扩大,跟 POSIX 规范有所差别,在应用时要再查看各自工具的帮忙文档。

正则表达式介绍

查看下面的正则表达式在线链接阐明,对正则表达式介绍如下,前面会有相应的中文阐明:

  • Regular Expressions (REs) provide a mechanism to select specific strings from a set of character strings.
  • The Basic Regular Expression (BRE) notation and construction rules in Basic Regular Expressions apply to most utilities supporting regular expressions. Some utilities, instead, support the Extended Regular Expressions (ERE) described in Extended Regular Expressions.
  • The concatenated set of one or more BREs or EREs that make up the pattern specified for string selection.
  • The use of regular expressions is generally associated with text processing. REs (BREs and EREs) operate on text strings; that is, zero or more characters followed by an end-of-string delimiter (typically NUL). Some utilities employing regular expressions limit the processing to lines; that is, zero or more characters followed by a newline character.
  • In the regular expression processing described in this specification, the newline character is regarded as an ordinary character and both a period and a non-matching list can match one.
  • Those utilities (like grep) that do not allow newline characters to match are responsible for eliminating any newline character from strings before matching against the RE.
  • The interfaces specified in this specification set do not permit the inclusion of a NUL character in an RE or in the string to be matched.
  • Matching is based on the bit pattern used for encoding the character, not on the graphic representation of the character. This means that if a character set contains two or more encodings for a graphic symbol, or if the strings searched contain text encoded in more than one codeset, no attempt is made to search for any other representation of the encoded symbol.
  • The search for a matching sequence starts at the beginning of a string and stops when the first sequence matching the expression is found, where first is defined to mean “begins earliest in the string”. If the pattern permits a variable number of matching characters and thus there is more than one such sequence starting at that point, the longest such sequence will be matched. For example: the BRE bb* matches the second to fourth characters of abbbc.

即,正则表达式提供了一个从字符串中选取特定子字符串的机制,分为根本正则表达式(BRE)、扩大正则表达式(ERE)两种模式。

正则表达式通常用于文本处理,在文本字符串上进行操作,基于字符编码值进行逐字匹配,要求被解决的字符串以空字符 NUL 结尾。局部应用正则表达式的工具要求以换行符结尾,便于逐行解决,跟 POSIX 规范的定义有所不同。

一般来说,空字符 NUL 的 ASCII 编码值为 0,在 C 语言 中写为 '\0'

在 POSIX 规范中,不容许匹配 NUL 这个空字符,换行符被当成一般字符解决,能够匹配到换行符。而局部工具(例如 grep)会去掉字符串开端的换行符,匹配不到行末换行符。

即,正则表达式通过 BRE 表达式、或者 ERE 表达式指定要匹配的字符串模式。反对正则表达式的工具能够基于所给的字符串模式从文本中找到合乎该模式的字符串。

留神 :正则表达式是基于字符的编码值进行匹配,而不是基于字符的图形示意来匹配。对于具备多种字符编码集的字符来说,要特地留神这一点。

例如中文有 UTF-8 编码、Unicode 编码、GBK 编码等。如果一个蕴含中文汉字的文件是 GBK 编码格局,而解析正则表达式的工具应用 UTF-8 编码来匹配这个文件,就会匹配不到。

因为同一个汉字在不同的编码集中的编码值不一样,应用不同编码集的编码值来进行匹配,显然不相等。

正则表达式在匹配时,从所给字符串的结尾开始查找匹配,默认遇到第一个匹配的内容就进行匹配。

如果所给正则表达式容许匹配变长字符串(例如通过 * 来匹配间断多个字符),那么会改成匹配到最长内容为止。

例如,对于 “AaaaaaA” 字符串来说,正则表达式 “aa” 在匹配到后面两个字符 a 时就进行匹配,匹配的内容是结尾 aa 子字符串。

而正则表达式 aa* 会始终匹配到最初一个字符 a 为止,匹配的内容是 “aaaaa” 子字符串。

根本正则表达式

根本正则表达式(Basic Regular Expression,在很多文档中常缩写为 BRE)次要由传统字符(Ordinary Characters)、特殊字符(Special Characters)、方括号 [] 表达式、子表达式等组成:

  • 传统字符是能够匹配本身的单个字符。例如,在正则表达式中,字符‘a’就示意它本身。
  • 特殊字符具备非凡的含意,不示意本身的字符,如果要匹配特殊字符本身,须要用反斜线 \ 进行本义。
  • 方括号 [] 表达式用于匹配一串字符汇合中的单个字符。留神,在理论匹配时,方括号表达式只匹配一个字符,只是这个字符能够是不同的字符。
  • 子表达式(subexpression)是用 \(\) 括起来的表达式,把多个字符集合起来,以便反向援用表达式进行援用。

根本正则表达式的特殊字符

根本正则表达式反对的特殊字符比拟少。POSIX 规范定义的根本正则表达式只反对 . [\ * ^ $ 这六个特殊字符,而且这些字符要在特定上下文环境中才具备非凡含意。具体阐明如下。

. [\ 这三个字符呈现在方括号 [] 表达式之外时,才具备非凡含意,呈现在方括号 [] 表达式之内没有非凡含意。

  • 点号 . 的非凡含意是能够匹配任意一个字符,但不包含空字符 NUL。对于那些要求以换行符结尾的工具来说,点号 . 匹配不到行末的换行符。
  • 反斜线 \ 的非凡含意是对特殊字符进行本义,示意特殊字符本身。例如,点号 . 具备非凡含意,如果要匹配点号‘.’ 这个字符,须要写为 \.。当 \ 前面跟着左小括号 (、右小括号 )、左大括号 {、右大括号 }、或者数字 1 到 9 时,另有非凡含意,前面会具体阐明。
  • 左大括号 [ 是预留的特殊字符,如果在方括号 [] 表达式之外独自提供一个左大括号,没有用 \ 对它本义,且没有呈现匹配的右大括号,具体后果未定义(undefined)。未定义的后果个别会报错。即,不要应用一个独自的左大括号。要匹配左大括号本身,要在方括号表达式之外用 \[ 进行本义。

除了上面提到的三种状况之外,星号 * 具备非凡含意,其含意是匹配零个或间断多个后面的上一个 BRE 表达式。

一个独自的传统字符也是一个 BRE 表达式。例如,a* 匹配空字符串、”aa” 字符串、”aaaaa” 字符串等。

星号 * 没有非凡含意的状况阐明如下:

  • 呈现在方括号 [] 表达式内,没有非凡含意。
  • 呈现在整个 BRE 表达式结尾的第一个字符、或者跟在整个 BRE 表达式结尾的 ^ 字符之后,没有非凡含意。因为此时在 * 后面没有字符,匹配为空。
  • 紧跟在子表达式结尾的 \( 之后、或者紧跟在结尾的 \(^ 之后,没有非凡含意。

^ 这个字符只在上面两种状况下呈现时,才具备非凡含意:

  • 呈现在方括号 [] 表达式之外,用于示意匹配字符串结尾。在实现时,也能够用在子表达式结尾的 \( 之后的第一个字符。例如,^ab 表达式能够匹配 abcdef 字符串结尾的 ab,但不匹配 cdefab 字符符开端的 ab
  • 呈现在方括号 [] 表达式结尾的第一个字符,紧跟在左大括号 [ 之后,示意不匹配方括号内的所有字符。例如,[^abc] 表达式不匹配字符 a、b、c,会匹配这三个字符之外的任意一个字符。

$ 字符只有呈现在整个 BRE 表达式开端的最初一个字符时才有非凡含意,用于示意匹配字符串开端。

在实现时,也能够用在子表达式开端 \) 后面的最初一个字符。例如,ab$ 表达式能够匹配 cdefab 字符串开端的 ab,但不匹配 abcdef 字符串结尾的 ab

当同时应用 ^$ 时,能够全词匹配一个字符串。例如,^abcdef$ 表达式只能匹配 abcdef 这个字符串。

POSIX 规范在形容 ^$ 的作用对象时,用的是 string 这个单词,也就是字符串,比拟少用 line 这个单词。

这里可能会带来一个误会,认为能够用 ^$ 来匹配字符串两头子字符串的结尾、或者结尾,这个是不能匹配的。

例如,有一个 “This is a test string.” 字符串,它由几个单词组成,单词之间用空格隔开。

用正则表达式来匹配这个字符串时,字符串结尾的单词是 “This” 子字符串,能够用 ^This 进行匹配,然而用 ^is 匹配不到两头的 “is” 子字符串。

^ 限定指向被匹配字符串的结尾,不能指向被匹配字符串外面用空格隔开的某个子字符串的结尾。

能够了解为,进行正则表达式匹配时,会传入匹配模式,和被匹配的整个字符串,该字符串外面可能带有空格,空格隔开了一些子字符串。

那么 ^ 只对应被匹配字符串的结尾,不能对应被匹配字符串外面子字符串的结尾。

留神 :这里形容的是 POSIX 规范定义的根本正则表达式特殊字符。局部工具可能进行了一些扩大,反对更多的特殊字符,局部特殊字符的含意可能会产生扭转,在理论应用时,要再参考工具本身的文档阐明。

例如,GNU grep 帮忙文档把特殊字符称为元字符(meta-character)。grep 命令应用 GNU BRE 的规定,增加了一些扩大,能够用 \< 来匹配字符串两头某个单词的结尾,用 \> 来匹配字符串两头某个单词的开端。同时,还扩大反对了 \+\| 等特殊字符。

具体能够参考 GNU grep 的在线链接阐明:https://www.gnu.org/software/…

方括号表达式

方括号 [] 表达式(bracket expression)用于匹配 [] 内字符汇合的任意一个字符,所给的字符汇合不能为空。

例如,[ab] 能够匹配字符 a、或者字符 b,但只能匹配一个字符。如果要匹配 ab 这两个字符,要写为 [ab][ab]

因为所给的字符汇合不能为空,右大括号 ] 紧跟在左大括号 [ 之后、或者紧跟在 [^ 之后时,并不是一个残缺的方括号表达式,此时的右大括号 ] 示意匹配它本身,还须要再加上一个右大括号 ] 才是残缺的表达式。

例如,只写为 [] 不是一个残缺的方括号表达式,要写为 []] 才是一个残缺的方括号表达式,第一个右大括号 ] 示意它本身,该表达式匹配‘]’这个字符。

grep 命令测试如下:

cat retestfile
This is a ] char.
$ grep '[]]' retestfile
This is a ] char.
$ grep '[]' retestfile
grep: Unmatched [ or [^
$ grep ']' retestfile
This is a ] char.

能够看到,grep '[]]' retestfile 命令能匹配到 ] 这个字符本身。

grep '[]' retestfile 命令执行报错,提醒左大括号没有配对,也就是短少闭合的右大括号。

grep ']' retestfile 命令也能匹配到 ] 这个字符本身,不须要用 \ 进行本义。

在下面列出的根本正则表达式特殊字符中并不包含 ] 这个字符。

基于这个行为,要在方括号表达式内匹配右大括号‘]’这个字符时,它必须写在字符汇合的结尾,不能写在字符汇合两头,也不能用 \ 进行本义。

在方括号表达式内,反斜线 \ 没有本义的性能,[\] 就是一个残缺的表达式,匹配‘’这个字符本身。

[\]] 是两个表达式,由 [\] 方括号表达式和 ] 字符所组成,会匹配 \] 字符串,不能匹配单个‘’字符,也不能匹配单个‘]’字符。

grep 命令测试如下:

$ cat retestfile
This is a ] char.
This is a \ char.
$ grep '[\]]' retestfile
$ grep '[\]' retestfile
This is a \ char.

能够看到,grep '[\]]' retestfile 命令什么都匹配不到,在 retestfile 文件中没有 \] 这个子字符串。

grep '[\]' retestfile 匹配到 \ 这个字符本身。

在方括号 [] 表达式内,如果 ^ 是结尾的第一个字符,紧跟在左大括号 [ 之后,则整个表达式会匹配除了 [] 内字符汇合之外的任意一个字符。

如果 ^ 没有紧跟在左大括号 [ 之后,则只示意‘^’字符本身。

例如,[^ab] 表达式匹配除了字符 a、字符 b 之外的任意一个字符。而 [a^b] 表达式匹配 a、^、b 这三个字符中的任意一个,只匹配一个字符。

字符类表达式

字符类表达式(character class expression)用于指定某一类字符汇合。

后面提到,方括号表达式在 [] 内要提供非空的字符汇合,字符类表达式就能够用于提供特定类型的字符汇合。

字符类表达式的写法是 [:name:]。这里的 [::] 称之为 bracket-colon delimiters,是整个表达式的一部分。

name 指定具体的字符类别。POSIX 规范定义了 12 个字符类型,阐明如下:

字符类别 含意
[:alnum:] 字母字符和数字字符 (能够匹配中文字符)
[:alpha:] 字母字符 (能够匹配中文字符)
[:blank:] 空白字符,特指空格和 tab 字符,不蕴含换行符
[:cntrl:] 控制字符
[:digit:] 数字字符
[:graph:] 图形字符,包含字母、数字、标点符号,不包含空格
[:lower:] 小写字符 (不能匹配中文字符)
[:print:] 可打印字符,包含字母、数字、标点符号、和空格
[:punct:] 标点符号,也包含运算符、各种括号等
[:space:] 所有空白字符 (空格,制表符,换行符,回车符)
[:upper:] 大写字符 (不能匹配中文字符)
[:xdigit:] 十六进制数字 (0-9,a-f,A-F)

局部工具扩大反对更多的字符类别,例如 [:word:] 等,能够再查看对应工具的阐明文档。

字符类表达式只是一类字符汇合,它本身不能用于匹配字符,必须用在方括号表达式外面,能力用来匹配字符汇合中的任意一个字符,只匹配一个字符。

留神 :字符类表达式的 [::] 是整个表达式的一部分,要辨别于方括号表达式的 [] 这两个括号。要把整个字符类表达式再放到方括号 [] 外面,才是一个无效的方括号表达式。

例如,写成 [[:alpha:]] 的模式。用 grep 命令测试如下:

$ grep [:alpha:] retestfile
grep: character class syntax is [[:space:]], not [:space:]
$ grep [[:alpha:]] retestfile
This is a ] char.
This is a \ char.

能够看到,grep [:alpha:] retestfile 命令执行报错,提醒正确的语法格局是把 [:space:] 再放到一个方括号 [] 外面。

这里打印的 [:space:] 是一个举例的提醒,跟该命令提供的 [:alpha:] 无关。

grep [[:alpha:]] retestfile 命令没有执行报错,它会匹配到各个字母、或数字。

范畴表达式

范畴表达式(range expression)示意两个字符之间的所有字符汇合,包含这两个字符自身。这两个字符通过连字符 - 隔开。

例如,A-Z 这个范畴表达式示意所有大写英文字母的汇合。

范畴表达式只示意一类字符汇合,它本身不能用于匹配字符,必须用在方括号表达式外面,能力用来匹配字符汇合中的任意一个字符。

例如,[A-Z] 示意匹配任意一个大写英文字母,只匹配一个字符。

留神 :范畴表达式左边字符的字母程序必须高于右边字符,也就是只能升序,不能降序。

例如,Z-A 不是一个无效的范畴表达式。用 grep 命令测试如下:

$ grep [Z-A] retestfile
grep: Invalid range end

能够看到,grep [Z-A] retestfile 命令报错,提醒有效的完结范畴。

在方括号表达式内,如果要匹配连字符‘-’本身,它必须写在字符汇合结尾(或者跟在结尾的 ^ 之后)、字符汇合开端、或者作为范畴表达式的完结字符。

例如,[-ac][ac-] 都示意匹配 a、c、- 这三个字符中的任意一个。[^-ac][^ac-] 都示意匹配除了 a、c、- 这三个字符之外的任意一个字符。

[%--] 示意匹配在 % 和 – 之间的任意一个字符,包含这两个字符本身,这里的第二个‘-’是范畴表达式的完结字符。

范畴表达式能够应用中文汉字,然而程序范畴不那么直观,不是基于拼音字母程序来抉择范畴,而是基于汉字编码值的程序来抉择范畴。

grep 命令测试如下:

$ cat retestfile
一
二
三
四
五
六
七
八
九
十
$ grep [一 - 十] retestfile
一
二
三
五
六
七
八
九
十
$ grep [冰 - 雨] retestfile
四
十 

能够看到,grep [一 - 十] retestfile 命令没有匹配到‘四’这个汉字。

‘一’对应的 Unicode 编码值是 \u4e00,‘十’对应的 Unicode 编码值是 \u5341,‘四’对应的 Unicode 编码值是 \u56db。即,‘四’的编码值不在‘一’到‘十’之间的范畴内,所以没有匹配到。

子表达式

子表达式(subexpression)是应用 \(\) 括起来的表达式,被括起来的字符汇合放弃原有的含意。

例如在子表达式 \(\) 内,字符 a 还是匹配它本身,点号 . 示意匹配任意一个字符,能够用反斜线 \ 来本义,能够用方括号 [] 表达式来匹配字符汇合中的任意一个字符,等等。

比拟特地的是 ^ 字符。当 ^ 呈现在子表达式结尾、紧跟在 \( 之后时,是用于匹配字符串结尾,还是匹配‘^’字符本身,是可选行为。POSIX 规范说是取决于具体的实现,也就是由工具本身决定。这可能会带来移植性问题。

应用 grep 命令测试子表达式如下:

$ cat retestfile
This is a ] char.
This is a \ char.
$ grep '\(]\)' retestfile
This is a ] char.
$ grep '\(\\\)' retestfile
This is a \ char.
$ grep '\(Th.s\)' retestfile
This is a ] char.
This is a \ char.
$ grep '\([]\]\)' retestfile
This is a ] char.
This is a \ char.

能够看到,\(]\) 示意匹配‘]’字符。\(\\\) 示意匹配‘’字符,子表达式内的特殊字符具备非凡含意,所以要用 \\ 来本义,能力匹配到‘’字符本身。

\(Th.s\) 能够匹配 “This” 字符串、”Thas” 字符串、”Thys” 字符串等等,在 Ths 之间能够是任意一个字符。

\([]\]\) 在子表达式内应用了方括号表达式 []\] 来匹配‘]’字符、或者‘’字符。

子表达式能够嵌套应用。例如写为 \(\(ab\).c\) 的模式。

因为子表达式内的字符汇合放弃原有的含意,把字符汇合放到子表达式内看起来是多余的。

其实,子表达式能够作为一个整体,配合反向援用表达式、或者配合区间表达式来应用。如前面的阐明所示。

反向援用表达式

反向援用表达式(back-reference expression)用于反向援用后面的子表达式,其写法是 \nn 必须是 1 到 9 之间的数字,包含 1 和 9。\n 会匹配从整个表达式结尾数起的第 n 个子表达式对应的内容,最多只能匹配到第 9 个子表达式。但还是能够提供 10 个、以及更多的子表达式,只是不能通过反向援用表达式来援用到。

留神 :反向援用表达式不是从 \n 表达式本身往前数起,而是从整个表达式结尾数起。

例如,对 \(ab\)\(ef\)\(mn\)\1\3 这个表达式来说,\1 匹配从右边数起的第一个 \(ab\) 子表达式的内容,并不是匹配在它后面的第一个 \(mn\) 子表达式内容。

\3 匹配从右边数起第三个 \(mn\) 子表达式的内容。

grep 命令举例如下:

$ cat retestfile
ab cd ef ab cd ef
$ grep '\(ab\) \(cd\) \(ef\) \1 \2 \3' retestfile
ab cd ef ab cd ef

\(ab\) \(cd\) \(ef\) \1 \2 \3 这个正则表达式中,\1 匹配结尾第一个 \(ab\) 子表达式的内容,也就是匹配 “ab” 字符串。

\2 匹配结尾第二个 \(cd\) 子表达式的内容,也就是匹配 “cd” 字符串。

\3 匹配结尾第三个 \(ef\) 子表达式的内容,也就是匹配 “ef” 字符串。

整个正则表达式对应的字符串是 “ab cd ef ab cd ef”,而 retestfile 文件的第一个行,就是这个字符串,匹配胜利,grep 命令打印出匹配行。

区间表达式

区间表达式(interval expression)指定在它后面的一个表达式要匹配多少次,其写法是 \{m\}\{m,\}、或者 \{m,n\}mn 的值要满足 0 <= m <= n 这个范畴。

  • \{m\} 指定要准确匹配 m 个前一个表达式。例如,a\{3\} 匹配三个字符 a,也就是 “aaa” 字符串。
  • \{m,\} 指定至多匹配 m 个前一个表达式。例如,a\{3,\} 至多匹配三个字符 a,能够匹配更多的字符 a。
  • \{m,n\} 指定至多匹配 m 个前一个表达式,最多匹配 n 个前一个表达式,包含 n 自身。例如,a\{3,5\} 至多匹配三个字符 a,最多匹配五个字符 a,即能够匹配 “aaa”、”aaaa”、”aaaaa” 这三个字符串。

区间表达式能够联合子表达式应用,例如写为 \(ab\)\{2\} 会匹配 “abab” 字符串。

应用 grep 命令测试如下:

$ cat retestfile
abab
aaa
$ grep 'a\{3\}' retestfile
aaa
$ grep '\(ab\)\{2\}' retestfile
abab

扩大正则表达式

扩大正则表达式(Extended Regular Expressions,,在很多文档中常缩写为 ERE)在根本正则表达式上进行了扩大,反对更多的特殊字符,局部特殊字符的应用场景产生了变动,特殊字符是否须要应用 \ 进行本义的场景也产生了变动。具体阐明如下。

扩大正则表达式的特殊字符

扩大正则表达式反对根本正则表达式定义的六个 . [\ * ^ $ 特殊字符,并新增 () { | + ? 六个特殊字符,总共反对十二个特殊字符。对这些特殊字符的含意和应用场景具体阐明如下。

. [\ ( 这四个字符呈现在方括号 [] 表达式之外时,具备非凡含意,呈现在方括号 [] 表达式之内没有非凡含意。当右小括号 ) 呈现在方括号 [] 表达式之外,且后面有配对的左小括号 ((也是在方括号 [] 表达式之外)时,具备非凡含意。

  • 扩大正则表达式的点号 . 非凡含意跟根本正则表达式雷同,能够参考下面的阐明。
  • 扩大正则表达式的反斜线 \ 非凡含意跟根本正则表达式雷同,能够参考下面的阐明。要留神的是,扩大正则表达式反对更多的特殊字符。在扩大正则表达式中,\( 匹配‘(’字符本身。而根本正则表达式中,\( 是子表达式的结尾,根本正则表达式就用 ( 来匹配字符‘(’本身。
  • 扩大正则表达式的左大括号 [ 非凡含意跟根本正则表达式雷同,能够参考下面的阐明。
  • 左小括号 ( 和右小括号 ) 把它们外面的字符优先组合到一起,小括号外部的字符放弃原有的含意不变,跟根本正则表达式的子表达式用法相似。
  • 在左小括号 ( 之后紧跟着右小括号 ),即小括号 () 外部为空的状况,是未定义的行为。
  • 尽管右小括号 ) 须要匹配后面的左小括号 ( 才具备非凡含意,然而一个独自的 ) 并不能匹配‘)’字符本身,还是须要用 \) 进行本义来示意‘)’字符本身。

* + ? { 这四个字符在方括号表达式之外,具备非凡含意。

  • 扩大正则表达式的星号 * 非凡含意跟根本正则表达式雷同,能够参考下面的阐明。
  • 加号 + 的非凡含意是匹配一个或间断多个后面的上一个 BRE 表达式。一个独自的传统字符也是一个 BRE 表达式。例如,a+ 匹配一个字符 a、”aa” 字符串、”aaaaa” 字符串等。
  • 问号 ? 的非凡含意是匹配零个或一个后面的上一个 BRE 表达式。一个独自的传统字符也是一个 BRE 表达式。例如,a? 匹配空字符串,或者一个字符 a。
  • *+? 的匹配后果都依赖前一个字符,如果这三个特殊字符呈现在表达式结尾,没有前一个字符时,具体后果未定义。
  • 左大括号 { 是扩大正则表达式预留的特殊字符,应用一个独自的左大括号 {、且前面没有配对的右大括号 } 是未定义的行为。

竖线 | 字符在方括号表达式之外,具备非凡含意。其含意是示意在 | 前后的两个模式字符串是可选的。

例如,abc|efg 示意匹配 “abc” 字符串、或者匹配 “efg” 字符串。

留神 :在正则表达式中,多个字符间断写在一起,默认用到了一个 concatenation 操作符。

例如,ab 这个写法是字符 a 和字符 b 的组合,默认用 concatenation 操作符进行组合。

concatenation 操作符的优先级高于 | 这个特殊字符,所以在 abc|efg 表达式中,在 | 右边的字符 a、b、c 先组合成字符串 “abc”,在 | 左边的字符 e、f、g 也组合成 “efg” 字符串,再进行 | 的非凡解决,匹配 “abc” 字符串、或者匹配 “efg” 字符串。

如果 | 特殊字符的优先级高于 concatenation 操作符,就变成匹配 “abefg” 字符串、或者匹配 “abefg” 字符串。

基于这个优先级问题,| 字符个别会跟小括号 () 一起组合应用,小括号内的表达式会优先组合。

例如,a(b|c)d 示意匹配 “abd” 字符串、或者匹配 “acd” 字符串。如果不加小括号,写为 ab|cd,会变成匹配 “ab” 字符串、或者匹配 “cd” 字符串。

在扩大正则表达式中,^ 这个字符只在上面两种状况下呈现时,才具备非凡含意:

  • 呈现在方括号 [] 表达式之外,用于示意匹配字符串结尾。例如,^ab 表达式、或者 (^ab) 表达式能够匹配 abcdef 字符串结尾的 ab,但不匹配 cdefab 字符符开端的 ab
  • 呈现在方括号 [] 表达式结尾的第一个字符,紧跟在左大括号 [ 之后,示意不匹配方括号内的所有字符。例如,[^abc] 表达式不匹配字符 a、b、c,会匹配这三个字符之外的任意一个字符。

留神 :根本正则表达式要求 ^ 呈现在表达式结尾第一个字符才具备非凡含意,如果呈现在表达式两头,没有非凡含意,能够匹配‘^’字符本身。然而在扩大正则表达式中,只有 ^ 呈现在方括号表达式之外,就具备非凡含意,即便呈现在表达式两头也有非凡含意,不能匹配‘^’字符本身,此时要匹配‘^’字符本身,须要用 \^ 进行本义。

例如,在 a^b 表达式中,^ 也是一个无效的特殊字符,然而这个表达式匹配不到任何字符串。因为没有字符串能够满足以字符 b 结尾,且后面还有字符 a 的状况。

grep 命令测试如下:

$ grep 'a^b' retestfile
a^b
$ grep -E 'a^b' retestfile

能够看到,grep 'a^b' retestfile 命令匹配到了 “a^b” 字符串,该命令没有加 -E 选项,应用根本正则表达式进行匹配,在 a^b 两头的 ^ 匹配字符‘^’本身。

grep -E 'a^b' retestfile 命令匹配不到任何内容,加了 -E 选项指定用扩大正则表达式,在 a^b 两头的 ^ 不能匹配字符‘^’本身,而是示意要匹配字符 b 结尾的字符串。然而后面多了一个字符 a,匹配不到这种字符串。

在扩大正则表达式中,$ 字符呈现在方括号 [] 表达式之外就具备非凡含意,用于示意匹配字符串开端。

例如,ab$ 表达式、或者 (ab$) 表达式能够匹配 cdefab 字符串开端的 ab,但不匹配 abcdef 字符串结尾的 ab

只有 $ 呈现在方括号表达式之外,就具备非凡含意,即便呈现表达式两头也有非凡含意,尽管 e$f 表达式会什么都匹配不到,但这是一个无效的表达式。

如果要匹配字符‘$’本身,须要用 \$ 进行本义。

留神 :如后面阐明,grep 命令应用 GNU BRE 规定,而 GNU BRE 对根本正则表达式进行了扩大,也能够反对 +?| 这三个特殊字符,只是要写为 \+\?\| 的模式。

例如,在 GRE BRE 中,\+ 就示意匹配一个或间断多个后面的上一个 BRE 表达式。具体能够查看 GNU grep 在线链接的阐明。

特殊字符本义

当在方括号 [] 表达式之外应用特殊字符时,根本表达式的特殊字符要在特定上下文环境中才有非凡含意,当没有非凡含意时,能够间接用特殊字符来匹配本身。

例如,当 * 呈现在表达式结尾时,没有非凡含意,就能够匹配字符‘*’本身。

而扩大正则表达式在方括号 [] 表达式之外应用特殊字符时,如果要匹配特殊字符本身,所反对的十二个特殊字符都须要用 \ 进行本义。残缺的特殊字符本义列表如下:

\^      \.      \[\$      \(      \)
\*      \+      \?      \{\\     \|

即便局部特殊字符在特定上下文环境中没有非凡含意,也须要用 \ 进行本义。这个规定跟下面阐明的根本正则表达式特殊字符本义规定有所不同。

例如,在扩大正则表达式中,) 只有在后面有匹配的 ( 时才具备非凡含意。* 呈现在表达式结尾时,是未定义的行为,没有非凡含意。

grep -E 命令测试如下:

$ cat retestfile
*
)
$ grep -E ')' retestfile
grep: Unmatched ) or \)
$ grep -E '\)' retestfile
)
$ grep -E '*' retestfile
*
)
$ grep -E '\*' retestfile
*

grep -E 命令指定用扩大正则表达式,能够看到,匹配模式写为 ')' 会报错,提醒没有匹配的左小括号。

匹配模式写为 '\)' 就能匹配到‘)’字符。

匹配模式写为 '*' 时,打印了 retestfile 文件的所有行。看起来 '*' 被当成空字符串,grep 命令的匹配模式为空字符串时,会匹配所有行。

匹配模式写为 '\*' 时,就会匹配到‘*’字符。

扩大正则表达式和根本正则表达式的差别

根本正则表达式反对方括号 [] 表达式、子表达式 \(\)、反向援用表达式 \n、区间表达式 \{m,n\}

扩大正则表达式对这些表达式的反对状况、以及跟根本正则表达式之间的差别阐明如下:

  • 扩大正则表达式的方括号 [] 表达式规定和根本正则表达式雷同,包含字符类表达式、范畴表达式的用法也雷同。
  • 扩大正则表达式用两个小括号 () 特殊字符来组合外面的字符,POSIX 规范文档称之为 grouping,不再称之为 subexpression。这个小括号 () 组合跟根本正则表达式的 \(\) 子表达式作用类似,看起来只是写法不同。
  • 扩大正则表达式不反对反向援用表达式 \n
  • 扩大正则表达式的区间表达式写法是 {m}{m,}、或者 {m,n}。相比于根本正则表达式的写法,在大括号后面不须要增加 \ 字符,表达式的作用雷同。

正则表达式特殊字符大总结

上面总结根本正则表达式和扩大正则表达式的特殊字符、各个表达式的根本用法。为了简化阐明,这里只举例说明根本用法,残缺阐明能够查看后面的内容。

根本正则表达式的特殊字符列表

扩大正则表达式的特殊字符列表

留神 :局部工具反对应用 \d 匹配任意一位数字、用 \w 匹配任意一个字母或者数字、以及其余相似的模式。POSIX 规范对这些模式没有定义,这是其余工具本身的扩大。

正则表达式和通配符的区别

对于先接触 bash 通配符(wildcard)、再接触正则表达式的人来说,容易把通配符和正则表达式搞混,往往在 bash 中应用正则表达式呈现不预期的后果。上面阐明 bash 通配符的作用,并形容正则表达式和通配符之间的区别。

Bash 通配符的概念

Bash 自身不能解析正则表达式,目前也没有一个独立的正则表达式命令专门来解析、测试正则表达式,而是要在反对正则表达式的命令上应用,由这些命令本身来解析正则表达式,例如 grep、sed、awk、find 等命令。

Bash 对通配符的解决,产生在 bash 扩大阶段,先对命令行参数进行扩大,再进行解决。

局部扩大表达式的特殊字符跟正则表达式的特殊字符应用了同一个字符,含意也相近,导致容易呈现混同。

其实,在 man bash 的阐明外面,没有呈现 wildcard 这个单词。

在 bash 中,罕用 * 来匹配任意字符串,然而 bash 并没有把星号 * 称为 wildcard

这是从其余文档挪用过去的称说,例如 GNU make 在线链接 https://www.gnu.org/software/… 有如下阐明:

A single file name can specify many files using wildcard characters. The wildcard characters in make are‘*’,‘?’and‘[…]’, the same as in the Bourne shell.

所以,大部分文档所说的“bash 通配符”其实是 bash 扩大表达式各个特殊字符的统称,这些特殊字符大部分来自路径名扩大(Pathname expansion)和大括号扩大(Brace Expansion),bash 本身没有提到 wildcard 这个概念。

路径名扩大

查看 man bash 的 Pathname Expansion 大节阐明,对路径名扩大反对的特殊字符阐明如下:

  • 星号 * 能够匹配任意字符串,包含空字符串,具体匹配内容跟它后面的字符无关。而正则表达式的 * 是匹配零个或间断多个后面的一个字符。这两者都应用了星号 * 作为特殊字符,然而含意不同。
  • 问号 ? 匹配任意一个字符,具体匹配内容跟它后面的字符无关。而扩大正则表达式的 ? 是匹配零个或一个后面的一个字符。这两者的含意不同。
  • 方括号 [...] 匹配方括号内的任意一个字符。如果在左大括号 [ 之后的第一个是 ^,也就是写为 [^...] 的模式,示意匹配除了方括号内字符之外的任意一个字符。这个含意跟正则表达式雷同。在 bash 中,[^...] 也能够写为 [!...] 的模式。方括号内反对字符类表达式和范畴表达式。例如,[[:alpha]] 匹配任意一个字母。[A-Z] 匹配任意一个大写字母。

留神 :这里形容的路径名扩大特殊字符是用于扩大当前目录下的文件名,包含子目录名。所以,尽管 * 能够匹配任意字符串,然而理论扩大之后,它失去的字符串肯定是当前目录下文件名的一部分,而且路径名扩大表达式不能用引号括起来。如果不了解这一点,就很容易呈现误用。

举例说明如下:

$ cat retestfile
retestfile
renewtestfile
$ grep re*file retestfile
retestfile
$ grep "re*file" retestfile
$ grep 're*file' retestfile

能够看到,在 retestfile 文件中,有 “retestfile”、”renewtestfile” 这两行字符串。

依照 bash 的 * 能够匹配任意字符串的性能,可能认为 grep re*file retestfile 命令能够匹配这两行,也就是认为 re*file* 能够匹配到 “retestfile” 字符串两头的 “test”,也能匹配到 “renewtestfile” 字符串两头的 “newtest”。

然而打印后果只匹配了 “retestfile” 这一行。起因在于,当前目录下有一个 retestfile 文件,然而没有 renewtestfile 文件。

即,尽管 re*file* 的确能够匹配到 “renewtestfile” 字符串两头的 “newtest”,然而它匹配到的字符串起源是当前目录下文件名的一部分,bash 会对 re*file 进行扩大,失去匹配的当前目录下文件名,再把文件名作为参数传递给 grep 命令,grep 命令收到的参数外面并没有 * 这个字符,也不会基于这个字符的含意来匹配字符串。

事实上,如果当前目录下还有一个 renewtestfile 文件,grep re*file retestfile 命令也匹配不到 “retestfile”、”renewtestfile” 这两行字符串。

关上 bash 调试开关,测试如下:

$ set -x
$ grep re*file retestfile
+ grep --color=auto renewtestfile retestfile retestfile
retestfile:renewtestfile
retestfile:renewtestfile
$ set +x

能够看到,grep re*file retestfile 命令扩大之后的后果是 grep --color=auto renewtestfile retestfile retestfile

基于 grep 命令的格局,这个命令要查找的字符串是 “renewtestfile”,前面的两个 retestfile 参数都是要查找的文件名,也就是查找了两次 retestfile 文件,所以打印后果只匹配到 renewtestfile 这一行,并打印了两次。

grep re*file retestfile 命令匹配不到 “retestfile”、”renewtestfile” 这两行字符串后,常见的误区是认为要用引号把 re*file 括起来,然而下面的 grep "re*file" retestfile 命令和 grep 're*file' retestfile 命令什么都没有都打印。

在 bash 中,用引号(无论是双引号,还是单引号)把 * 括起来后,不会再进行路径名扩大,此时,* 匹配的是字符‘*’本身,在 retestfile 文件中没有 “re*file” 这个字符串,所以什么都没有匹配到。

总的来说,在 bash 的路径名扩大中,*?[...] 这几个特殊字符的扩大后果来自于当前目录下的文件名,并把扩大后果作为参数传递给被执行的命令。

如果匹配到多个后果,会导致理论传递给命令的参数个数发生变化,可能会导致命令解决参数时遇到不预期的后果。

大括号扩大

查看 man bash 的 Brace Expansion 大节阐明,对大括号扩大反对的特殊字符阐明如下:

Brace expansion is a mechanism by which arbitrary strings may be generated. This mechanism is similar to pathname expansion, but the filenames generated need not exist. Patterns to be brace expanded take the form of an optional preamble, followed by either a series of comma-separated strings or a sequence expression between a pair of braces, followed by an optional postscript. The preamble is prefixed to each string contained within the braces, and the postscript is then appended to each resulting string, expanding left to right.
A sequence expression takes the form {x..y[..incr]}, where x and y are either integers or single characters, and incr, an optional increment, is an integer. When integers are supplied, the expression expands to each number between x and y, inclusive.

即,大括号扩大是一种用于生成任意字符串的机制,相似于路径名扩大,然而大括号扩大的后果不依赖于已有的文件名。其格局有两种:

  • preamble{string1,string2,string3,…}postscript:preamble 是一个可选的前缀字符串,postscript 是一个可选的后缀字符串,一旦提供,所有扩大后果都会带上相应的前缀、或后缀。大括号外面的字符串列表会一一进行组合,失去多个字符串。例如,a{d,c,b}e 会扩大成 “ade”、”ace”、”abe” 三个字符串。
  • preamble{x..y[..incr]}postscript:preamble 是一个可选的前缀字符串,postscript 是一个可选的后缀字符串,一旦提供,所有扩大后果都会带上相应的前缀、或后缀。大括号外面的 xy 要么是整数,要么是单个字母。而 ..incr 是一个可选的整数增量值。当提供的是整数时,这个表达式扩大成在 x 和 y 之间的每一个数字,蕴含 x 和 y。当提供的是字母时,这个表达式依据词典程序扩大成 x 和 y 之间的每一个字母,蕴含 x 和 y。例如,a{1..4}c 会扩大成 “a1c”、”a2c”、”a3c”、”a4c” 四个字符串。

具体执行后果举例如下:

$ echo a{d,c,b}e
ade ace abe
$ echo a{1..4}c
a1c a2c a3c a4c
$ echo a{1..6..2}c
a1c a3c a5c

跟路径名扩大不同,大括号扩大的后果跟当前目录下的文件名无关。

通配符和正则表达式的比照

基于后面形容的几个 bash 通配符特殊字符,和正则表达式的特殊字符比照如下。

特殊字符 通配符含意 正则表达式含意
* 用于路径名扩大,匹配任意字符串,匹配后果来自于当前目录下的文件名 匹配零个或间断多个前一个字符,跟通配符含意不同
? 用于路径名扩大,匹配任意一个字符,匹配后果来自于当前目录下的文件名 匹配零个或一个前一个字符,跟通配符含意不同
[..] 用于路径名扩大,匹配方括号内的任意一个字符,匹配后果来自于当前目录下的文件名 匹配字符方括号内的任意一个字符,跟通配符含意相近
[^..] 用于路径名扩大,不匹配方括号内的任意一个字符,从当前目录下的文件名中过滤掉匹配后果 不匹配方括号内的任意一个字符,跟通配符含意相近
[!..] 用于路径名扩大,跟 [^..] 含意雷同 正则表达式不反对这个写法
{s1,s2,..} 用于大括号扩大,应用大括号内的字符串列表一一进行组合 正则表达式不反对在大括号内提供三个或更多的字符串。扩大正则表达式反对相似的 {m,n} 写法,至多匹配 m 个前一个字符,最多匹配 n 个前一个字符
{n1..n2} 用于大括号扩大,顺次失去 n1 到 n2 之间的数字 正则表达式不反对这个写法

留神 :通配符的方括号表达式和正则表达式的方括号表达式相似,能够在方括号外面提供字符类表达式或范畴表达式。

具体举例如下:

$ ls *.[c-h]
dfa.c  dfa.h
$ ls *.[[:alpha:]]
dfa.c  dfa.h

这里应用 ls 命令举例,ls 命令不反对解析正则表达式,能够确认是由 bash 解析所给的表达式。

这里的 [c-h] 属于路径名扩大表达式,会匹配字母 c 到 字母 h 之间的任意一个字母,蕴含 c 和 h 本身。相似的,[[:alpha:]] 也是路径名扩大表达式,匹配任意一个字母。

另外,通配符的字符类表达式反对用 [:word:] 来匹配字母、数字、或下划线 _ 之中的任意一个字符。POSIX 规范的正则表达式没有定义 [:word:] 这个字符类表达式。

因为 bash 通配符的局部特殊字符和正则表达式的特殊字符用了同一个字符,在 bash 中应用这些字符时,肯定要确认是想当成通配符解决,还是当成正则表达式解决

如果要当成通配符解决,那么所给的特殊字符不能用引号括起来,由 bash 对这些特殊字符进行扩大。

如果要当成正则表达式解决,那么所给特殊字符要用引号括起来,防止 bash 优先当成通配符解决,扩大成了其余内容。

如果不不便用引号括起来,就要在 bash 中用 \ 进行本义,且不要加引号,\ 在引号内不能进行本义。

正文完
 0