如何使用grep,regex或perl按照模式提取字符串


90

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

    <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的解决方案很好。


5
不必害羞,欢迎来到这里!
Benoit


感谢大家的有用评论。对于XML格式不正确,我深表歉意。为了简化起见,我删除了一些标签。
wrangler

Answers:


167

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

佩尔

使用Perl,您可以使用n 选项逐行循环并在匹配的情况下打印捕获组的内容:

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

GNU grep

如果您具有改进的grep版本,例如GNU grep,则可能有-P可用的 选项。此选项将启用类似Perl的正则表达式,从而使您可以使用\K它。它将重置匹配位置,因此零宽度之前的任何内容。

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

o 选项使grep仅打印匹配的文本,而不打印整个行。

Vim-文本编辑器

另一种方法是直接使用文本编辑器。使用Vim,完成此操作的各种方法之一是删除没有的行 name=,然后从结果行中提取内容:

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

标准grep

如果由于某种原因您无权使用这些工具,则可以使用标准grep实现类似的操作。但是,如果不四处看看,稍后将需要进行一些清理:

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

关于保存结果的说明

在以上所有命令中,结果将发送至stdout。重要的是要记住,您始终可以通过以下方式将它们通过管道传递到文件中来保存它们:

> result

到命令末尾。


12
环顾四周(在GNU中grep):grep -Po '.*name="\K.*?(?=".*)'
已暂停,直到另行通知。

@丹尼斯·威廉姆森,太好了。我相应地更新了答案,但.*撇开它们,希望您不要生我的气。我想问一下,与“除"”以外的其他内容相比,贪婪的匹配有什么好处吗?不要以为这是一场战斗,我只是好奇,而且我不是正则表达式专家。另外,\K小费,真的很好。谢谢丹尼斯。
sidyll '02

2
我为什么会生气?没有.*,您可以做grep -Po '(?<=name=").*?(?=")'。该\K可用于速记,但实际上它仅需要如果匹配到它的左边是可变长度。在这种情况下,使用环顾四周的原因非常明显。Ungreedy操作看起来有点整洁([^"]*.*?,你不必重复锚角色,我不知道速度在很大程度上取决于上下文,我想我希望这是有帮助的。。
暂停,直至另行通知。

@丹尼斯·威廉姆森:当然,先生,这里有很多有用的信息。我认为我保留\K(研究后)并删除的原因.*是相同的:使其看起来更漂亮(更简单)。而且我从未想过要使用.*?从某个地方学到的“传统方式”来代替。但是这里的不贪心确实是有道理的。谢谢丹尼斯,最良好的祝愿。
sidyll'2

+1用于描述命令。如果您可以更新答案以解释正则表达式的“ [...]”部分,将不胜感激。
2014年



5

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

程序

#!/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


2

这是使用HTML整洁和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

糟糕,sed命令必须先于整洁的命令:

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

如果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 '"'
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.