grep -E
为什么对于负空白不能按我的预期工作? IE[^\s]+
我写了一个正则表达式来解析我的.ssh/config
grep -Ei '^host\s+[^*\s]+\s*$' ~/.ssh/config
# cat ~/.ssh/config
Host opengrok-01-Eight
Hostname opengrok-01.company.com
Host opengrok-02-SIX
Hostname opengrok-02.company.com
Host opengrok-03-forMe
Hostname opengrok-03.company.com
Host opengrok-04-ForSam
Hostname opengrok-04.company.com
Host opengrok-05-Okay
Hostname opengrok-05.company.com
Host opengrok-05-Okay opengrok-03-forMe
IdentityFile /path/to/file
Host opengrok-*
User root
我得到的是
Host opengrok-01-Eight
Host opengrok-03-forMe
Host opengrok-05-Okay
Host opengrok-05-Okay opengrok-03-forMe
六号和山姆在哪里!
我花了一些时间才意识到[^\s*]+
ie 匹配任何不是空格或*
, 1 次或多次的内容实际上是匹配任何不是\
, s
, *
, 1 次或多次的内容!
修复非常简单,因为正则表达式适用于 rex101.com(使用 perl),即-E
switch-P
# grep -Pi '^host\s+[^*\s]+\s*$' ~/.ssh/config
Host opengrok-01-Eight
Host opengrok-02-SIX
Host opengrok-03-forMe
Host opengrok-04-ForSam
Host opengrok-05-Okay
让我害怕的是我多年来一直grep -E
在许多脚本中使用,但之前没有发现这一点。也许我只是运气好,但更可能的是我的测试用例错过了那个边缘情况!
问题:
- 除了更改为用于
grep -P
所有扩展正则表达式之外,我应该如何grep -E
为这种情况编写我的正则表达式? - 是否还有其他我遗漏的令人讨厌的问题
-E
或者如果我使用的话会咬我的问题-P
?
grep (GNU grep) 3.1
Copyright (C) 2017 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Written by Mike Haertel and others, see <http://git.sv.gnu.org/cgit/grep.git/tree/AUTHORS>.
在 Windows 10 上运行,WSL 运行 Ubuntu 18.04 (bash) ...但我从正确的 Linux 安装中得到了相同的结果
答案1
的补码\s
是\S
, not [^\s]
which (在 的帮助下-i
)将 'SIX' 和 'Sam' 从结果中排除,因为它们包含文字s
。
如何grep -i
处理以“host”开头,后跟一个或多个空格以及一系列一个或多个字符直到行尾,其中不能*
存在文字或空格:
grep -Ei '^host[[:space:]]+[^*[:space:]]+$' file
Host opengrok-01-Eight
Host opengrok-02-SIX
Host opengrok-03-forMe
Host opengrok-04-ForSam
Host opengrok-05-Okay
答案2
解释\s
为空白是 GNU Grep 的扩展。它没有定义在POSIX。BSD 查询例如,不识别\s
为空格。 Perl 正则表达式也是 POSIX 的扩展,但 BSD 和 GNU 都提供它。对于完全可移植的表达式,您应该使用[[:space:]]
。
GNU Grep 手册有点松散地说“大多数元字符在括号表达式中失去了它们的特殊含义”。你已经发现这\s
是其中之一,它实际上是由POSIX(再次)特殊字符.
、*
、[
和\
在括号表达式中应该失去其特殊含义。但您仍然可以便携式使用[:space:]
。
那么,回答你的两个问题,
我应该如何
grep -E
为这个案例写我的文章?
grep -Ei '^host[[:space:]]+[^*[:space:]]+[[:space:]]*$'
是否还有其他我遗漏的令人讨厌的问题
-E
或者如果我使用的话会咬我的问题-P
?
.*?
一个常见的错误是在没有标志的情况下尝试非贪婪的 Perl -P
。
$ echo 'AB 14 34' | grep -Eo '^.*?4'
AB 14 34
$ echo 'AB 14 34' | grep -Po '^.*?4'
AB 14
$ echo 'AB 14 34' | grep -o '^.*?4'
{nothing}
最后一句话:BRE 和 ERE和 PRE 都是不同的。了解你的正则表达式!