如何使用 grep、regex 或 perl [duplicate] 按照模式提取字符串

How to extract string following a pattern with grep, regex or perl [duplicate]

提问人:wrangler 提问时间:2/23/2011 最后编辑:Michaelwrangler 更新时间:11/26/2019 访问量:302991

问:

这个问题在这里已经有答案了:
2年前关闭。

社区去年审查了是否重新讨论这个问题,并关闭了它:

重复这个问题已经得到回答,不是唯一的,也没有与另一个问题区分开来。

我有一个文件,看起来像这样:

    <table name="content_analyzer" primary-key="id">
      <type="global" />
    </table>
    <table name="content_analyzer2" primary-key="id">
      <type="global" />
    </table>
    <table name="content_analyzer_items" primary-key="id">
      <type="global" />
    </table>

我需要提取后面引号中的任何内容,即 和 。name=content_analyzercontent_analyzer2content_analyzer_items

我是在 Linux 机器上执行此操作的,因此使用 sed、perl、grep 或 bash 的解决方案就可以了。

正则表达式 perl sed html 解析 文本提取

评论

6赞 Benoit 2/23/2011
不用害羞,欢迎光临!
9赞 Christoffer Hammarström 2/23/2011
我觉得不链接到 stackoverflow.com/questions/1732348/ 是错误的......
1赞 wrangler 2/24/2011
感谢大家的有用评论。对于XML格式不正确,我深表歉意。为了简化起见,我删除了一些标签。

答:

2赞 Benoit 2/23/2011 #1

这可以做到:

perl -ne 'if(m/name="(.*?)"/){ print $1 . "\n"; }'
6赞 Matt Shaver 2/23/2011 #2

正则表达式为:

.+name="([^"]+)"

然后分组将在 \1

5赞 shawnhcorey 2/23/2011 #3

如果您使用的是 Perl,请下载一个模块来解析 XML:XML::Simple、XML::Twig 或 XML::LibXML不要重新发明轮子。

评论

3赞 bvr 2/23/2011
请注意,OP 给出的示例格式不正确(例如),因此大多数 XML 解析器只是抱怨并死亡。<type="global"
5赞 Alan Haggai Alavi 2/23/2011 #4

为此,应使用 HTML 解析器而不是正则表达式。一个使用 HTML::TreeBuilder 的 Perl 程序:

程序

#!/usr/bin/env perl

use strict;
use warnings;

use HTML::TreeBuilder;

my $tree = HTML::TreeBuilder->new_from_file( \*DATA );
my @elements = $tree->look_down(
    sub { defined $_[0]->attr('name') }
);

for (@elements) {
    print $_->attr('name'), "\n";
}

__DATA__
<table name="content_analyzer" primary-key="id">
  <type="global" />
</table>
<table name="content_analyzer2" primary-key="id">
  <type="global" />
</table>
<table name="content_analyzer_items" primary-key="id">
  <type="global" />
</table>

输出

content_analyzer
content_analyzer2
content_analyzer_items
221赞 sidyll 2/23/2011 #5

由于您需要匹配内容而不将其包含在结果中(必须 匹配,但它不是所需结果的一部分)某种形式的 需要零宽度匹配或分组捕获。这是可以做到的 使用以下工具轻松:name="

Perl的

使用 Perl,您可以使用该选项逐行循环并打印 捕获组的内容(如果匹配):n

perl -ne 'print "$1\n" if /name="(.*?)"/' filename

GNU grep

如果你有一个改进的 grep 版本,比如 GNU grep,你可能有 可用的选项。此选项将启用类似 Perl 的正则表达式, 允许您使用哪个是速记后视。它将重置 匹配位置,因此它之前的任何内容都是零宽度。-P\K

grep -Po 'name="\K.*?(?=")' filename

该选项使 grep 仅打印匹配的文本,而不是 整条线。o

Vim - 文本编辑器

另一种方法是直接使用文本编辑器。使用 Vim,其中之一 实现此目的的各种方法是删除不带的行,然后从生成的行中提取内容:name=

:v/.*name="\v([^"]+).*/d|%s//\1

标准 grep

如果您由于某种原因无法访问这些工具,请 使用标准 GREP 也可以实现类似的效果。然而,没有外观 围绕它稍后将需要进行一些清理:

grep -o 'name="[^"]*"' filename

关于保存结果的说明

在上面的所有命令中,结果将被发送到 。它 重要的是要记住,您始终可以通过将其管道连接到 文件附加:stdout

> result

到命令的末尾。

评论

12赞 Dennis Williamson 2/23/2011
Lookarounds(在 GNU 中):grepgrep -Po '.*name="\K.*?(?=".*)'
0赞 sidyll 2/23/2011
@Dennis威廉姆森,太好了。我相应地更新了答案,但把两者都放在一边,希望你不要生我的气。我想问一下,你认为不贪婪的匹配比“除了”有什么好处吗?不要把这当成一场战斗,我只是好奇,我不是正则表达式专家。另外,小费,真的很好。谢谢丹尼斯。.*"\K
3赞 Dennis Williamson 2/23/2011
我为什么会生气?没有 ,您可以做 。可以用于速记,但只有当其左侧的匹配是可变长度时才需要它。在这种情况下,使用环绕的原因相当明显。不贪婪的操作看起来更整洁一些( versus 并且您不必重复锚点字符。我不知道速度。我认为,这在很大程度上取决于具体情况。我希望这对您有所帮助。.*grep -Po '(?<=name=").*?(?=")'\K[^"]*.*?
0赞 sidyll 2/23/2011
@Dennis Williamson:当然,先生,这里有很多有用的信息。我认为我保留(在研究它之后)并删除它的原因是一样的:让它看起来很漂亮(更简单)。而且我从来没有想过用我从某个地方学到的“传统方式”来代替。但这里的不贪婪确实是有道理的。谢谢丹尼斯,最好的祝愿。\K.*.*?
0赞 lreeder 3/5/2014
+1 用于描述命令。如果您能更新您的答案以解释正则表达式的“[...]”部分,将不胜感激。
2赞 mitma 3/17/2011 #6

这是一个使用HTML tidy和xmlstarlet的解决方案:

htmlstr='
<table name="content_analyzer" primary-key="id">
<type="global" />
</table>
<table name="content_analyzer2" primary-key="id">
<type="global" />
</table>
<table name="content_analyzer_items" primary-key="id">
<type="global" />
</table>
'

echo "$htmlstr" | tidy -q -c -wrap 0 -numeric -asxml -utf8 --merge-divs yes --merge-spans yes 2>/dev/null |
sed '/type="global"/d' |
xmlstarlet sel -N x="http://www.w3.org/1999/xhtml" -T -t -m "//x:table" -v '@name' -n
1赞 mitma 3/17/2011 #7

哎呀,sed 命令当然必须在 tidy 命令之前:

echo "$htmlstr" | 
sed '/type="global"/d' |
tidy -q -c -wrap 0 -numeric -asxml -utf8 --merge-divs yes --merge-spans yes 2>/dev/null |
xmlstarlet sel -N x="http://www.w3.org/1999/xhtml" -T -t -m "//x:table" -v '@name' -n
0赞 Carlos Lindado 12/2/2017 #8

如果 xml(或一般文本)的结构是固定的,最简单的方法是使用 .对于您的具体情况:cut

echo '<table name="content_analyzer" primary-key="id">
  <type="global" />
</table>
<table name="content_analyzer2" primary-key="id">
  <type="global" />
</table>
<table name="content_analyzer_items" primary-key="id">
  <type="global" />
</table>' | grep name= | cut -f2 -d '"'