为什么正则表达式“.+”不能按预期工作?

为什么正则表达式“.+”不能按预期工作?
[root@localhost opt]# cat cfg
key = value
[root@localhost opt]# grep 'key\s*=\s*.+' cfg
[root@localhost opt]# 

我的意图是:该=符号后面可以跟零个或多个空格,但后面必须跟一个或多个非空格字符。

为什么不输出该行key = value

答案1

观察:

$ grep 'key\s*=\s*.+' cfg
$ grep 'key\s*=\s*.\+' cfg
key = value
$ grep -E 'key\s*=\s*.+' cfg
key = value

在基本正则表达式(BRE,默认值)中,+表示加号。作为 GNU 扩展,可以使用 来表示一个或多个前一个字符\+?{|和也是如此(。除非用反斜杠转义,否则这些在 BRE 下都被视为普通字符。

如果您使用扩展正则表达式,则规则会发生变化-E。对于 ERE,不需要反斜杠,普通+表示一个或多个前一个字符。在 ERE 下,\+表示普通的正态加号。

答案2

key\s*=\s*.+

是 GNU ERE 语法(假设您想要\s匹配任何空格字符,并+匹配一个或多个前面的原子),因此您需要 的 GNU 实现grep并传递该-E选项。

然而,即使那样也没有多大意义

第一的

grep 'key\s*=\s*.+'

功能上等价于

grep 'key\s*=\s*.'

因为如果一个字符串匹配anything.+,那么它也匹配anything.,反之亦然。

另外,空格字符也是一个字符。自从\s*比赛以来0或更多空格字符,key\s*=\s*.在功能上等同于key\s*=.(包含 的行key<optional-spaces>=<one-character-space-or-not>)。

在这里你想要:

grep 'key\s*=\s*\S'

要求在 的右侧至少找到一个非空格字符=,其功能相当于:

grep 'key\s*=.*\S'

请注意,它key = foo也匹配 but nonkey = foo。如果您希望key仅在行的开头找到 ,则需要使用锚点来请求^

grep '^key\s*=.*\S'

或者使用-x正则表达式来匹配整行:

grep -x 'key\s*=.*\S.*'

请注意, 的标准等效项\s[[:space:]]( [^[:space:]]for \S)。

满足该要求的另一种方法是使用某些正则表达式中的扩展运算符(例如 PCRE)来防止回溯。

key=\s*.匹配,key= 因为正则表达式引擎\s*贪婪地遍历了 后面的空格字符=,找到 1,然后意识到它无法匹配,.因为它到达了行尾,然后回溯尝试使用较少的匹配项\s(在这种情况下为 0),以便下一个.可以匹配(此处为空格字符)。

使用 PCRE,就像使用-PGNU 选项一样grep,您可以编写:

 grep -P '^key\s*=(?>\s*).'

(?>...)语法可以防止回溯。因此,\s*将在无法回溯的情况下吃掉尽可能多的空格字符,因此只有在空格后面至少有一个非空格字符时才会匹配。

$ printf 'key=%s\n' '' ' ' ' a' | grep '^key\s*=\s*.'
key=
key= a
$ printf 'key=%s\n' '' ' ' ' a' | grep -P '^key\s*=(?>\s*).'
key= a
$ printf 'key=%s\n' '' ' ' ' a' | grep '^key\s*=.*\S'
key= a

相关内容