为什么grep的正则表达式不支持 d
1. 概述
正则表达式 (Regex) 在 Linux 命令行中被广泛使用。许多常用命令都支持正则表达式,例如 grep 、sed 和awk 。
我们中的一些人可能遇到过特定正则表达式不适用于 Linux 命令的情况——例如,包含*\d*的模式——但是,相同的正则表达式适用于 Java 或 Python。这可能会让我们感到困惑。
在本教程中,让我们仔细看看这类问题并解释它为什么会发生。
2. 问题介绍
像往常一样,让我们通过一个例子来理解这个问题。首先,让我们创建一个文本文件作为我们的输入:
$ cat input.txt
Linux is awesome!
This server is running the Linux kernel 5.16.5-arch1-1.
It has many powerful commands.
input.txt文件包含三行。
我们知道 Regex [0-9]匹配一位数字。因此,命令grep ‘[0-9]’ input.txt应该匹配input.txt 文件中的第二行:
$ grep '[0-9]' input.txt
This server is running the Linux kernel 5.16.5-arch1-1.
此外,我们可能已经了解到“ \d是*[0-9]的缩写形式” 。所以,让我们用 \d 替换grep*命令中的正则表达式,然后再试一次:
$ grep '\d' input.txt
It has many powerful commands.
如上面的输出所示,似乎grep无法将* \d 识别为[0-9]。相反,它将\d*视为文字字母’ d ‘。因此,仅匹配最后一行。
如果我们用 sed或 awk 测试相同的正则表达式,我们可以得到相同的结果:
$ sed -n '/\d/p' input.txt
It has many powerful commands.
$ awk '/\d/' input.txt
awk: cmd. line:1: warning: regexp escape sequence `\d' is not a known regexp operator
It has many powerful commands.
此外, awk命令显式地抛出一条警告消息,指出*’\d’*是未知的。
但是,如果我们用 Java、Python 或 PHP 测试相同的 Regex 和输入文件,我们可以获得预期的输出。
那么,为什么**Linux 命令不支持*\d*呢?**接下来,让我们弄清楚。
3. BRE、ERE 和 PCRE
要回答这个问题,我们应该了解不同的 Regex 风格。有三种常用的正则表达式语法——BRE、ERE 和 PCRE:
- BRE——基本正则表达式
- ERE——扩展正则表达式
- PCRE – Perl 兼容的正则表达式
BRE 来得最早。它的功能和表现力有限。然后,BRE 扩展到 ERE。后来,PCRE 加入了 Regex 派对,拥有丰富的强大功能。
我们不会深入研究每种正则表达式语法,并使其成为完整的正则表达式教程。相反,我们将通过一些示例来讨论 BRE、ERE 和 PCRE 之间的一些区别。
3.1. BRE
正如我们之前提到的,BRE 是最古老的正则表达式语法。顾名思义,它只支持非常基本的功能。例如,标准 POSIX BRE 不支持以下功能:
- ‘|’ – 交替
- “?” – 0 或 1
- ‘+’ - 1 个或更多
- ‘\s’ – 空格的简写
此外,我们需要转义“{m, n}”(占有量词)和“(…)”(分组)以赋予它们特殊的含义。例如,“[0-9]{2,4}”匹配两个、三个或四个数字。
引入 ERE 后,大多数 Regex 引擎,例如 GNU BRE,都支持 BRE 中的一些简写,例如 ’ \s ‘。此外,**BRE 也支持 |、? 和 +。但是,我们需要逃避它们才能给它们带来特殊的意义。**例如,BRE “a|b” 匹配 a 或 b。
3.2. ERE
ERE 扩展了 BRE。**使用 ERE,我们不需要转义 |、?、+、() 和 {} 来赋予它们特殊的含义。**例如,“a|b”匹配 a 或 b , “[0-9]{2,4}” 匹配两个、三个或四个数字。
但是,如果我们想从字面上匹配这些字符,我们需要对它们进行转义。例如,“a|b”匹配文字字符串“a|b”。
3.3. 聚合酶链反应
最初,PCRE 是一个实现 Perl Regex 引擎的库。后来,由于 Perl 普及了 Regex,它成为了一种流行的 Regex 风格。许多其他实用程序和编程语言都具有与 PCRE 兼容的 Regex 引擎,例如 Java、Python 和 PHP。
PCRE 的语法比 BRE 和 ERE 更加强大和灵活。让我们看一下仅在 PCRE 中可用的一些功能:
- 环顾 - 正面和负面的前瞻/后视
- 非贪心匹配 - *? , +? , 和*{m, }?*
- 区分大小写/不区分大小写的匹配 - (?i)和(?-i)
- 匹配数字或非数字字符的简写 - \d和*\D*
现在,我们知道当我们使用’ \d ‘时我们正在使用PCRE。只有与 PCRE 兼容的 Regex 引擎才能正确解释 PCRE。
接下来,让我们看一下 Linux 命令以及它们支持的 Regex 风格。
4. grep、sed、awk的正则表达式
在本节中,我们将以广泛使用的 GNU grep、GNU sed和 GNU awk作为示例。
4.1. GNU grep
** grep默认处于 GNU BRE 匹配模式。**也就是说,如果我们不设置选项,它只支持 BRE 语法。例如,我们可以匹配包含“ awesome ”或“ powerful ”的行:
$ grep 'awesome\|powerful' input.txt
Linux is awesome!
It has many powerful commands
正如我们在上面的命令中看到的,我们已经转义了“|” 字符赋予它特殊的含义。
** grep允许我们使用*-E选项将模式解释为 ERE**。让我们用-E*选项做同样的测试
$ grep -E 'awesome|powerful' input.txt
Linux is awesome!
It has many powerful commands.
请注意,我们不应该逃避 ‘|’ 当我们将*-E选项传递给 grep时。否则, grep将搜索文字 ‘|’ 特点。 *GNU grep支持-P选项来解释 PCRE 模式。*因此,如果我们希望grep命令匹配 PCRE,例如“ \d ”,我们应该使用-P*选项:
$ grep -P '\d' input.txt
This server is running the Linux kernel 5.16.5-arch1-1.
正如我们所见, grep支持“ \d ”,但我们必须使用正确的选项。
4.2. GNU sed和 GNU awk
与grep一样, sed默认使用 BRE。此外,我们可以通过*-r*选项告诉 sed使用 GNU ERE 进行模式匹配:
$ sed -n '/awesome\|powerful/p' input.txt
Linux is awesome!
It has many powerful commands.
$ sed -nr '/awesome|powerful/p' input.txt
Linux is awesome!
It has many powerful commands
但是,sed不支持 PCRE。因此,sed无法解释“\d”。
另一方面,GNU awk支持 GNU ERE。同样, ** awk也不支持 PCRE**。
因此,我们不能在sed和 awk中使用 PCRE 独有的功能。