[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,就像使用-P
GNU 选项一样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