【问题标题】:How to overcome greedy match everything when looking for a particular string later?以后查找特定字符串时如何克服贪婪匹配所有内容?
【发布时间】:2017-03-01 02:22:00
【问题描述】:
echo "A number is about to show up 1 and now I want to parse 365 guys and some extra junk"  | sed -E 's/.*([0-9]+) guys.*/\1/g'

上述命令目前只输出5。本质上,我想在一个随机句子中解析“人”的数量,该句子可能在人的数量之前有数字(或没有......我也想只解析echo "365 guys")。我的.*36 匹配,并阻止它出现在\1 中。如何编写 sed 命令(或任何其他正则表达式/perl/awk)来完成我想要的?

【问题讨论】:

    标签: bash perl awk sed


    【解决方案1】:

    在 Perl 中使用“节俭”量词 *?

    perl -pe 's/.*?([0-9]+) guys.*/$1/'
    

    【讨论】:

    • .* 根本没有理由出现
    • @Borodin:有,你想替换它。当然还有其他方法可以解决这个问题。
    • 啊,我没发现这是替换
    • 我也很乐意提供perl -lne 'm/(\d+) guys/ and print $1'
    【解决方案2】:

    使用 GNU grep

    $ grep -Po '\b[0-9]+(?= guys\b)' <<<"365 guys or 366 guys, but not foo12 guys."
    365
    366
    
    • -P actives 支持 PCREs,从而启用高级正则表达式功能。
    • -o 指定只打印输入行的匹配部分。
    • \b 仅匹配单词边界,包括行首;
      这可以防止匹配不是独立数字而是其他词的一部分的数字,例如foo365 guys,以及以guys开始的词,例如guysanddolls
    • (?= guys) 是一个前瞻断言,它匹配封闭的子表达式,但不将其包含在返回的匹配字符串中。

    正如演示的那样,这可能会匹配给定行上的 多个 模式,每个提取的数字都打印在其自己的输出行上。
    如果不希望这样做,则不能使用grep,因为-o 总是 返回一行的所有匹配项;请参阅下面的perl 命令以获取解决方案。


    Sobrique对choroba的回答的评论启发,这里是上述grep命令的perl等效

    $ perl -lne 'print for m/\b(\d+) guys\b/g' <<<"365 guys or 366 guys, but not foo12 guys."
    365
    366
    

    只需省略g 以仅匹配每行最多1 个数字。

    【讨论】:

      【解决方案3】:

      由于您的号码前面有一个空格,您可以将其作为正则表达式的一部分:

      echo "A number is about to show up 1 and now I want to parse 365 guys and some extra junk"  | sed -E 's/.* ([0-9]+) guys.*/\1/g'
      
      # => 365
      

      【讨论】:

      • 很好的建议,但我的句子可能并不总是在空格前面。 echo "365 guy and some extra junk" 也是一个有效的输入。我会让这个问题更加有力。不过谢谢!
      【解决方案4】:

      在 Bash 中:

      $ s="A number is about to show up 1 and now I want to parse 365 guys and some extra junk"
      $ [[ $s =~ ([0-9]+)\ +guys.*$ ]] && echo ${BASH_REMATCH[1]}
      365
      

      或者,使用 awk:

      $ echo "$s" | awk '/guys/{for (i=1;i<=NF;i++) if ($i=="guys" && $(i-1)+0==$(i-1)) print $(i-1)}'
      365
      

      【讨论】:

        【解决方案5】:

        使用标准 sed 正则表达式,如果您反转字符串并匹配,您可以从贪婪匹配中受益

        echo ... | rev | sed -E 's/.*syug ([0-9]+).*/\1/g' | rev
        

        显然这是一个黑客,但绝望的时候......

        【讨论】:

          【解决方案6】:

          @Andrew Cassidy:@try:

             echo "A number is about to show up 1 and now I want to parse 365 guys and some extra junk"  |
          awk '/guys/{print VAL;exit} {VAL=$0}' RS=" "
          

          【讨论】:

            【解决方案7】:

            这可能对你有用(GNU sed):

             sed -r 's/.*\b([0-9]+) guys.*/\1/' file
            

            或许:

             sed -r 's/.*\<([0-9]+) guys.*/\1/' file
            

            使模式的数字部分与单词边界匹配。

            【讨论】:

              猜你喜欢
              • 2019-11-26
              • 2011-05-04
              • 1970-01-01
              • 2013-06-09
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-10-28
              相关资源
              最近更新 更多