【问题标题】:How to do a non-greedy match in grep?如何在 grep 中进行非贪婪匹配?
【发布时间】:2011-03-02 22:25:50
【问题描述】:

我想 grep 最短的匹配,模式应该是这样的:

<car ... model=BMW ...>
...
...
...
</car>

... 表示任何字符,输入是多行。

【问题讨论】:

标签: regex shell command-line grep regex-greedy


【解决方案1】:

您正在寻找非贪婪(或懒惰)的匹配。要在正则表达式中获得非贪婪匹配,您需要在量词之后使用修饰符 ?。例如,您可以将.* 更改为.*?

默认情况下grep 不支持非贪婪修饰符,但您可以使用grep -P 来使用Perl 语法。

【讨论】:

  • eegg: dot all 修饰符也称为多行。它是改变“。”的修饰符。匹配行为以包含换行符(通常不包含)。 grep 中没有这样的修饰符,但pcregrep 中有。
  • 更正:在大多数支持它的正则表达式风格中,允许 . 匹配换行符的模式称为 DOTALLsingle-line 模式; Ruby 是唯一一个称其为 multiline 的。在其他风格中,multiline 是允许锚点(^$)在行边界处匹配的模式。 Ruby 没有等效模式,因为在 Ruby 中它们总是以这种方式工作。
  • -P 对我来说是一个全新的人,多年来我一直很高兴地挣扎,只使用-E ......浪费了这么多年! - 自我注意:重新阅读手册页作为(甚至更多!)常规的东西,你永远不会消化足够的开关和选项。
  • 在某些平台上(如 Mac OS X)grep 不支持-P,但如果您使用egrep,您可以使用.*? 模式来实现相同的结果。 egrep -o 'start.*?end' text.html
  • 作为@SaltyNuts 评论的扩展,Mac OS X 不支持-P,但-E 会调用egrep,因此建议的.*? 工作正常。
【解决方案2】:

实际上.*? 仅适用于perl。我不确定等效的 grep 扩展正则表达式语法是什么。幸运的是,您可以将 perl 语法与 grep 一起使用,因此 grep -P 可以工作,但与 egrep 相同的 grep -E 将无法工作(它会很贪心)。

另请参阅:http://blog.vinceliu.com/2008/02/non-greedy-regular-expression-matching.html

【讨论】:

  • grep -P 在 GNU grep 2.9 中不起作用——刚刚尝试过(它没有错误,只是默默地不应用 ?。有趣的是 not 类 i> 例如:env|grep '[^\=]*\='
  • 在 Darwin/OS X 10.8 Mountain Lion 中没有 grep -P 选项或 pgrep 命令,但 egrep 效果很好。
  • 我的 OS X 10.9 机器上有一个 pgrep 命令,但它是一个完全不同的程序,其目的是“按名称查找或通知进程”。
  • @robertotomás 在这里回复一个 6 年前的评论,但是....我也这么想,然后意识到我得到了多个非贪婪匹配。例如,在彩色终端上,您可以看到 ` echo "bbbbb" | grep -P 'b.*?b'` 返回 2 个匹配项。
【解决方案3】:

我的 grep 在尝试了此线程中的内容后有效:

echo "hi how are you " | grep -shoP ".*? "

只需确保在每一行都附加一个空格

(我的是逐行搜索吐词)

【讨论】:

  • -shoP 很好的助记符 :)
  • echo "bbbbb" | grep -shoP 'b.*?b' 有点学习经验。就明确的懒惰而言,唯一对我有用的东西。
【解决方案4】:

grep

对于grep 中的非贪婪匹配,您可以使用否定字符类。换句话说,尽量避免使用通配符。

例如,要从页面内容中获取所有指向 jpeg 文件的链接,您可以使用:

grep -o '"[^" ]\+.jpg"'

要处理多行,首先通过xargs 管道输入。为提高性能,请使用ripgrep

【讨论】:

  • 从来没有想过这样使用它。为我工作。
【解决方案5】:

对不起,我迟到了 9 年,但这可能对 2020 年的观众有用。

所以假设你有一个像"Hello my name is Jello" 这样的行。 现在,您要查找以'H' 开头并以'o' 结尾的单词,中间包含任意数量的字符。我们不想要线条,我们只想要文字。因此,我们可以使用以下表达式:

grep "H[^ ]*o" file

这将返回所有单词。它的工作原理是:它将允许所有字符而不是空格字符,这样我们可以避免在同一行中出现多个单词。

现在您可以将空格字符替换为您想要的任何其他字符。 假设最初的行是"Hello-my-name-is-Jello",那么你可以使用表达式获取单词:

grep "H[^-]*o" file

【讨论】:

    【解决方案6】:

    简短的回答是使用下一个正则表达式:

    (?s)<car .*? model=BMW .*?>.*?</car>
    
    • (?s) - 这使得多行匹配
    • .*? - 以懒惰的方式匹配任何字符,多次(最小 匹配)

    一个(稍微)更复杂的答案是:

    (?s)<([a-z\-_0-9]+?) .*? model=BMW .*?>.*?</\1>
    

    这将使得在以下文本中匹配 car1 和 car2 成为可能

    <car1 ... model=BMW ...>
    ...
    ...
    ...
    </car1>
    <car2 ... model=BMW ...>
    ...
    ...
    ...
    </car2>
    
    • (..) 表示捕获组
    • \1 在此上下文中匹配与最近匹配的相同文本 捕获组号 1

    【讨论】:

      【解决方案7】:

      我知道这有点过时了,但我只是注意到这行得通。它从我的输出中删除了清理和清理。

      > grep -v -e 'clean\-\?up'
      > grep --version grep (GNU grep) 2.20
      

      【讨论】:

        猜你喜欢
        • 2015-01-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-10-16
        • 2015-02-11
        • 2011-12-10
        • 1970-01-01
        相关资源
        最近更新 更多