这是我的示例文本。grep w
,grep ^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 选项