【问题标题】:How to extract string following a pattern with grep, regex or perl [duplicate]如何使用grep,regex或perl按照模式提取字符串[重复]
【发布时间】:2011-07-02 03:30:47
【问题描述】:

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

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

【问题讨论】:

  • 不用害羞,欢迎光临!
  • 感觉不链接stackoverflow.com/questions/1732348/…是不对的
  • 感谢大家提供有用的 cmets。对于 XML 格式不正确,我深表歉意。为了简化,我删除了一些标签。

标签: regex perl sed html-parsing text-extraction


【解决方案1】:

这可以做到:

perl -ne 'if(m/name="(.*?)"/){ print $1 . "\n"; }'

【讨论】:

    【解决方案2】:

    正则表达式是:

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

    那么分组将在 \1

    【讨论】:

      【解决方案3】:

      如果您使用 Perl,请下载一个模块来解析 XML:XML::SimpleXML::TwigXML::LibXML。不要重新发明轮子。

      【讨论】:

      • 请注意,OP 给出的示例格式不正确(例如&lt;type="global"),因此大多数 XML 解析器只会抱怨而死。
      【解决方案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
      

      【讨论】:

        【解决方案5】:

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

        Perl

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

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

        GNU grep

        如果您有 grep 的改进版本,例如 GNU grep,您可能有 -P 选项可用。此选项将启用类似 Perl 的正则表达式, 允许您使用\K,这是一个速记的lookbehind。它会重置 匹配位置,所以它之前的任何东西都是零宽度。

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

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

        Vim - 文本编辑器

        另一种方法是直接使用文本编辑器。使用 Vim,其中之一 实现这一点的各种方法是删除行而不 name= 然后从结果行中提取内容:

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

        标准 grep

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

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

        关于保存结果的说明

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

        > result
        

        到命令的末尾。

        【讨论】:

        • 环视(在 GNU 中 grep):grep -Po '.*name="\K.*?(?=".*)'
        • @Dennis Williamson,太好了。我相应地更新了答案,但将.* 放在一边,希望你不要生我的气。我想问一下,你认为非贪婪匹配比“除了"”有什么好处吗?不要把这当成一场战斗,我只是好奇而且我不是正则表达式专家。另外,\K 提示,非常好。谢谢丹尼斯。
        • 我为什么会生气?没有.*,您可以使用grep -Po '(?&lt;=name=").*?(?=")'\K 可用于速记,但只有在其左侧的匹配是可变长度时才需要它。在这种情况下,使用环视的原因是相当明显的。不贪婪的操作看起来更整洁([^"]*.*? 并且您不必重复锚字符。我不知道速度。这在很大程度上取决于上下文,我认为。我希望这会有所帮助。
        • @Dennis Williamson:当然,先生,这里有很多有用的信息。我认为我保留\K(在对其进行研究之后)并删除.* 的原因是相同的:让它看起来更漂亮(更简单)。而且我从未想过使用.*? 而不是我从某个地方学到的“传统方式”。但这里不贪婪真的很有意义。谢谢丹尼斯,祝你好运。
        • +1 用于描述命令。如果您能更新您的答案以解释正则表达式的“[...]”部分,将不胜感激。
        【解决方案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
        

        【讨论】:

          【解决方案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
          

          【讨论】:

            【解决方案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 '"'
            

            【讨论】:

              猜你喜欢
              • 2021-10-12
              • 2016-08-27
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-08-03
              • 2019-10-13
              相关资源
              最近更新 更多