使用grep和?实现非贪婪匹配

8

我正在编写一个bash脚本,用于分析html文件并想要获取每个单独的<tr>...</tr>标签中的内容。所以我的命令如下:

$ tr -d \\012 < price.html | grep -oE '<tr>.*?</tr>'

但是似乎 grep 给我返回的结果是:

$ tr -d \\012 < price.html | grep -oE '<tr>.*</tr>'

如何使.*变成非贪婪模式?

2
必须说明的是:你不能使用正则表达式解析HTML - glenn jackman
4个回答

19
如果你有 GNU Grep,你可以使用 -P 使匹配结果变成非贪婪的:
$ tr -d \\012 < price.html | grep -Po '<tr>.*?</tr>'
-P选项启用Perl兼容正则表达式(PCRE),这在使用Basic Regular Expression (BRE)和Extended Regular Expression (ERE)无法支持的非贪婪匹配时是必需的。如果您正在使用-P,您也可以使用look arounds来避免打印匹配中的标签。
$ tr -d \\012 < price.html | grep -Po '(?<=<tr>).*?(?=</tr>)'

如果你没有GNU grep,而且HTML格式正确,你可以这样做:

$ tr -d \\012 < price.html | grep -o '<tr>[^<]*</tr>'

注意:上面的示例不能处理<tr>标签内部的嵌套标签。


3
最后一个例子(使用“[^<]*”)不太可能奏效,因为“tr”内部不可避免地会包含“td”或“th”标签。 - glenn jackman
1
@glennjackman 说得好,我会把它留在答案中,因为这个通用原则可能对旁观者有用。 - Chris Seymour

4

非贪婪匹配不是 grep -E 支持的扩展正则表达式语法的一部分。如果您有 Perl/Python/Ruby 或其他程序,请改用 grep -P。或者可以考虑使用 pcregrep。

当然,如果您真的意味着

<tr>[^<>]*</tr>

你应该这样说;那样就可以使用普通的grep了。

你可以(费力地)扩展正则表达式来接受嵌套标签,而不是<tr>,但当然,最好使用适当的HTML解析器,而不是浪费大量时间重新发现为什么正则表达式不是正确的工具。


3

.*? 是 Perl 正则表达式。将您的 grep 更改为

grep -oP '<tr>.*?</tr>'

1
或者,如果他只想要tr标签的内容:grep -oP '(?<=<tr>).*?(?=</tr>)' -- 使用look-around来省略实际的标签。 - glenn jackman

3
尝试使用 Perl 风格的正则表达式。
$ grep -Po '<tr>.*?</tr>' input
<tr>stuff</tr>
<tr>more stuff</tr>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接