为什么 egrep 忽略负空格?

Mar*_*tin 4 grep regular-expression

为什么不能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
Run Code Online (Sandbox Code Playgroud)

我得到的是

Host opengrok-01-Eight
Host opengrok-03-forMe
Host opengrok-05-Okay
Host opengrok-05-Okay opengrok-03-forMe
Run Code Online (Sandbox Code Playgroud)

六和山姆在哪里!

我花了一些时间才意识到[^\s*]+ie 匹配任何不是空白或*, 1 次或多次实际上是匹配任何不是\,s*1 次或多次!

修复非常简单,因为该正则表达式适用于 rex101.com(使用 perl),即切换-E-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
Run Code Online (Sandbox Code Playgroud)

令我害怕的是我多年来一直grep -E在许多脚本中使用,但以前没有发现过。也许我只是很幸运,但更有可能的是我的测试用例错过了那个边缘案例!

问题:

  1. 除了更改为grep -P用于我所有的扩展正则表达式之外,我应该如何grep -E为这种情况编写我的?
  2. 是否有任何其他令人讨厌的问题是我一直遗漏的,-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>.
Run Code Online (Sandbox Code Playgroud)

在 Windows 10 上运行,WSL 运行 Ubuntu 18.04 (bash) ...但我从正确的 Linux 安装中得到了相同的结果

tha*_*isp 11

\sis的补码\S, not [^\s]which(在 的帮助下-i)从结果中排除了 'SIX' 和 'Sam',因为它们包含文字s.


如何grep -i为以“host”开头的行,后跟一个或多个空格和一个或多个字符的序列,直到行尾,其中不*存在文字或空格:

grep -Ei '^host[[:space:]]+[^*[:space:]]+$' file
Run Code Online (Sandbox Code Playgroud)
Host opengrok-01-Eight
Host opengrok-02-SIX
Host opengrok-03-forMe
Host opengrok-04-ForSam
Host opengrok-05-Okay
Run Code Online (Sandbox Code Playgroud)


Qua*_*odo 6

解释\s为空格是 GNU Grep 的扩展。它没有在POSIX 中定义。例如,BSD Grep不会识别\s为空格。Perl 正则表达式也是 POSIX 的扩展,但 BSD 和 GNU 都提供了它。对于完全可移植的表达式,您应该[[:space:]]改用。

GNU Grep 手册有些松散地说明“大多数元字符在括号表达式中失去了它们的特殊含义”。你已经发现\s是其中之一,它是由指定的其实POSIX(再次)的特殊字符.*[\应在括号表达式中失去它们的特殊含义。但是您仍然可以便携地使用[:space:].

所以,回答你的两个问题,

我应该如何grep -E为这个案例写我的?

grep -Ei '^host[[:space:]]+[^*[:space:]]+[[:space:]]*$'
Run Code Online (Sandbox Code Playgroud)

是否有任何其他令人讨厌的问题是我一直遗漏的,-E或者如果我使用它会咬我-P

一个常见的错误是在.*?没有-P标志的情况下尝试 Perl 非贪婪。

$ 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}
Run Code Online (Sandbox Code Playgroud)

最后一句话:BRE 和 ERE和 PRE 都是不同的。了解您的正则表达式!