如何在 grep 中进行非贪婪匹配?
Posted
技术标签:
【中文标题】如何在 grep 中进行非贪婪匹配?【英文标题】:How to do a non-greedy match in grep? 【发布时间】:2011-03-02 22:25:50 【问题描述】:我想 grep 最短的匹配,模式应该是这样的:
<car ... model=BMW ...>
...
...
...
</car>
... 表示任何字符,输入是多行。
【问题讨论】:
***.com/questions/1732348/1732454#1732454 【参考方案1】:您正在寻找非贪婪(或懒惰)的匹配。要在正则表达式中获得非贪婪匹配,您需要在量词之后使用修饰符 ?
。例如,您可以将.*
更改为.*?
。
默认情况下grep
不支持非贪婪修饰符,但您可以使用grep -P
来使用Perl 语法。
【讨论】:
eegg: dot all 修饰符也称为多行。它是改变“。”的修饰符。匹配行为以包含换行符(通常不包含)。 grep 中没有这样的修饰符,但pcregrep 中有。 更正:在大多数支持它的正则表达式风格中,允许.
匹配换行符的模式称为 DOTALL 或 single-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
【讨论】:
以上是关于如何在 grep 中进行非贪婪匹配?的主要内容,如果未能解决你的问题,请参考以下文章