为什么 `^[ ]{0,}` 不能与 Linux grep 一起使用?

为什么 `^[ ]{0,}` 不能与 Linux grep 一起使用?

这是我的示例文本。grep wgrep ^w并且grep '^[ ]w'工作得很好。

[user@linux ~]$ grep w text.txt
whitespace 0
 whitespace 1
  whitespace 2
[user@linux ~]$

[user@linux ~]$ grep ^w text.txt
whitespace 0
[user@linux ~]$

有1个空格

[user@linux ~]$ grep '^[ ]w' text.txt
 whitespace 1
[user@linux ~]$

有 2 个空格,但得到相同的输出

[user@linux ~]$ grep '^[  ]w' text.txt
 whitespace 1
[user@linux ~]$

根据https://regex101.com/,^[ ]{0,}是在行首查找空格的正确语法。然而,它在 Linux 上不能很好地与 GNU grep 配合使用。我收到错误Invalid regular expression

[user@linux ~]$ grep ^[ ]{0,}w text.txt
grep: Invalid regular expression
[user@linux ~]$

这些根本不返回任何东西

[user@linux ~]$ grep '^[ ]{0}w' text.txt
[user@linux ~]$ grep '^[ ]{1}w' text.txt
[user@linux ~]$ grep '^[ ]{2}w' text.txt
[user@linux ~]$ grep '^[ ]{0,}w' text.txt
[user@linux ~]$

问题:可以^[ ]{0,}与 GNU grep 一起使用吗?如果是,我以前的语法有什么问题?

答案1

这里有各种各样的问题。首先,该表达式的^[ ]w意思是:找到行的开头,然后正好是一个空格,然后是一个w。所以它实际上工作得很好。如果你想让它匹配一个或多个空格,你需要在[ ]字符类中添加一个限定符:

  $ grep '^[  ]\+w' text.txt
 whitespace 1
  whitespace 2

意思+是“一个或多个”。使用的默认正则表达式风格grep称为 BRE(基本正则表达式),在该正则表达式风格中,需要+转义,因此\+上面的*。或者,您可以通过传递标志来使用 ERE(扩展正则表达式)-E,或通过传递-P标志来使用 PCRE(Perl 兼容正则表达式)。使用这些正则表达式风格,您不需要转义+它即可充当量词:

$ grep -P '^[  ]+w' text.txt
 whitespace 1
  whitespace 2
$ grep -E '^[  ]+w' text.txt
 whitespace 1
  whitespace 2

下一个问题,也是更重要的一个问题,是您没有引用正则表达式。需要引用以确保正则表达式传递到grep 按原样并且不是首先由 shell 解释的。但是,由于您没有引用它,因此它在传递给 之前会被 shell 扩展grep。您可以使用选项set -x让 shell 打印它正在执行的操作来检查这一点:

$ set -x
$ grep ^[ ]{0,}w text.txt
+ grep '^[' ']0w' ']w' text.txt
grep: Invalid regular expression

^[首先,因为和之间有一个空格],shell 将其解释为两个单独的参数:^[]{0,}w。但它们{}在 shell 中用于大括号扩展。例如:

$ echo foo{a,b}
fooa foob

但是当扩展的第二部分为空时,您会得到:

$ echo foo{a,}
fooa foo

所以,展开式]{0,}w就变成:

$ echo ]{0,}w
]0w ]w

结果,正如您在set -x上面的输出中看到的,这三个参数是实际传递给的grep

'^[' ']0w' ']w'

但如果你确实引用了它们,那么在使用 BRE 时将需要对它们进行转义,就像+上面一样:

$ grep '^[ ]\{2\}w' text.txt
  whitespace 2

最后一点:[ ]与 完全相同,对单个字符使用字符类是没有意义的。

将所有这些放在一起,为了精确匹配行开头的一个空格,请使用:

$ grep '^ w' text.txt 
 whitespace 1

要匹配一个或多个,请使用:

$ grep '^ \+w' text.txt 
 whitespace 1
  whitespace 2

或者:

$ grep -E '^ +w' text.txt 
 whitespace 1
  whitespace 2

或者

$ grep -P '^ +w' text.txt 
 whitespace 1
  whitespace 2

要匹配特定的数字范围(例如 0、1 或 2 个空格):

$ grep '^ \{0,3\}w' text.txt 
whitespace 0
 whitespace 1
  whitespace 2

或者

$ grep -P '^ {0,3}w' text.txt 
whitespace 0
 whitespace 1
  whitespace 2

或者

$ grep -E '^ {0,3}w' text.txt 
whitespace 0
 whitespace 1
  whitespace 2

要匹配特定数字,请按如上{}所示设置该数字,或者仅重复该字符 N 次:

$ grep '^ \{2\}w' text.txt
  whitespace 2
$ grep '^ w' text.txt
 whitespace 1
$ grep '^  w' text.txt
  whitespace 2

始终引用您的正则表达式!


*实际上,在 POSIX BRE 中,+没有特殊含义,但 GNU 实现的 BREgrep确实可以识别它,如果它被转义的话。

答案2

在 BRE 中,在贪婪量词表达式中{0,},需要对大括号进行转义以实现所需的正则表达式匹配,并且始终引用你的正则表达式字符串。如果没有引号,shell 会尝试将自己的解析语法应用于提供的参数,并且在大多数情况下,参数会被分词,从而grep只能看到^[正则表达式的部分。

grep '^[ ]\{0,\}w' file

正则表达式参考:量词并选择 GNU BRE

\{n,\}其中n >= 0至少重复前一项n。贪婪,因此在尝试前一项匹配较少的排列之前,将匹配尽可能多的项,直到前一项仅匹配 n 次。

正如评论中所指出的,使用*相当于使用的修饰符\{0,\}

答案3

正确命令:

使用grep -E '^[ ]{0,}' text.txt

-E, --extended-regexp 将 PATTERN 解释为扩展正则表达式(ERE,见下文)。

其不工作的原因:

不要在正则表达式周围使用单引号,bash 将打开它,你的命令将变成

grep '^[' ] ]0 text.txt它将转换为带有正则表达式'^['的 grep 文件]]0以及text.txt

^[是错误的,因为特殊字符也[需要结束字符]

为什么-E选项:

{m,n} 是扩展的正则表达式,要使用它,grep 需要 -E 选项

相关内容