Contents

为什么grep的正则表达式不支持 d

1. 概述

正则表达式 (Regex) 在 Linux 命令行中被广泛使用。许多常用命令都支持正则表达式,例如 grepsed 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. grepsedawk的正则表达式

在本节中,我们将以广泛使用的 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 独有的功能。