【问题标题】:grep / awk / perl / sed - print all lines that match X, ignore lines that only have YX and include lines that have YX ZXgrep / awk / perl / sed - 打印所有匹配 X 的行,忽略只有 YX 的行并包括有 YX ZX 的行
【发布时间】:2014-11-14 02:14:40
【问题描述】:

我有一个文件 (tmp.txt),如下所示:

 first
 first rst
 allrst

printf "first\nfirst rst\nallrst\n" > tmp.txt;

我想要:

 first rst
 allrst

有没有办法搜索“rst”,但如果 rst 只在第一个找到,则排除匹配项?

我试过了:

awk '(/rst/ && /first/) || (/rst/ && !/first/)' tmp.txt

【问题讨论】:

    标签: regex perl awk sed grep


    【解决方案1】:

    使用不带-P 选项的grep

    grep 'rst' file | grep -v '^first$'
    first rst
    allrst
    

    使用grep -P

    grep -P '^(?!first$).*rst' file
    first rst
    allrst
    

    【讨论】:

      【解决方案2】:
      ^(?=.*(?<!fi)rst).*$
      

      在 Perl 中试试这个。参见演示。

      http://regex101.com/r/kP8uF5/13

      【讨论】:

        【解决方案3】:

        或vks回答的变体

        ^(.*(?<!fi)rst.*)$
        

        Demo

        ^               Start of string
        (               Capturing Group \1
          .             Any character except line break
          *             (zero or more)(greedy)
          (?<!          Negative Look-Behind
            fi          "fi"
          )             End of Negative Look-Behind
          rst           "rst"
          .             Any character except line break
          *             (zero or more)(greedy)
        )               End of Capturing Group \1
        $               End of string
        

        【讨论】:

          【解决方案4】:

          Perl 中的正则表达式是/^.*(?&lt;!fi)rst.*/m

           ^ .* 
           (?<! fi )
           rst .* 
          

          【讨论】:

            【解决方案5】:

            你没有定义,rst 必须在行尾,所以这可能符合你的要求:

            grep -P '(?<!fi)rst' file
            

            【讨论】:

              【解决方案6】:

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

              sed 'h;s/first//g;/rst/!d;g' file
              

              复制当前行。删除所有first 并检查rst 的剩余行。如果字符串在更改的行中,则打印副本,否则删除该行。

              【讨论】:

                【解决方案7】:

                这满足您的所有要求:

                perl -ne 'print if /^(?=.*first)(?=.*(?<!fi)rst)/; next if /first/; print if /rst/'
                
                1. 行有“第一个”和一个非第一个“rst”:打印它
                2. 行有“第一”:跳过它
                3. 行有“rst”:打印它

                或者,正如你的标题所暗示的,象征性地

                perl -ne '
                  BEGIN {$x="rst"; $y="fi"; $z="(?<!fi)"}
                  print if /^(?=.*$y$x)(?=.*$z$x)/; next if /$y$x/; print if /$x/
                '
                

                【讨论】:

                  【解决方案8】:

                  恕我直言,模式没有精确指定。如果该行包含afirstrstfirst,应该怎么办?所以我创建了两个版本。我假设每一行都包含字符串rst。不是,它不需要打印。我更喜欢 解决方案而不是 ,因为后者使用更多资源来启动,并且此任务并不真正需要它。

                  第一个版本检查所有包含rst,但不等于first 的单词。如果找到,则打印该行。

                  awk '/rst/ {
                    for(i=1;i<=NF&&$i~/rst/&&$i=="first";++i);
                  }i<=NF' inputfile
                  

                  输入文件:

                  first
                  first rst
                  allrst
                  afirst
                  rstfirst
                  

                  输出:

                  first rst
                  allrst
                  afirst
                  rstfirst
                  

                  另一种解决方案检查所有rst,然后添加前两个字符(如果适用)。如果结果字符串不是first,则打印该行。 (类似于sln 的负后视缓冲区 解决方案)

                  awk '/rst/ {
                    for(s=$0;i=index(s,"rst");s=substr(s,i+1)) 
                      if (i<2 || substr(s,i-2,5)!="first") {print; break}
                  }' inputfile
                  

                  输出:

                  first rst
                  allrst
                  rstfirst
                  

                  我希望这会有所帮助!

                  【讨论】:

                    【解决方案9】:
                    sed -n '1!{/rst/p}' tmp.txt
                    

                    如果不在第一行,则打印与该行中模式 rst 的匹配项,或者如果您想计算 rst 但不单独在 first 中 - 仅当 rst 也在另一个字符串中时:

                    sed -n '/[^f][^i]rst/p' tmp.txt 
                    

                    或者如果你想使用 Bash shell,你可以更灵活地满足类似的要求:

                    while read -r a; do  
                     num_first=$(echo "$a" | grep -c 'first');
                     num_rst=$(echo "$a" | sed 's/first//g' | grep -c 'rst'); 
                     if [[ $num_rst+1 -gt $num_first ]]; then 
                       echo "$a"; 
                     fi done < tmp.txt
                    

                    此代码先使用 grep 计数,然后使用 rst - 只有当 rst 大于字符串时才会打印字符串。

                    作为单行:

                    while read -r a; do num_first=$(echo "$a" | grep -c 'first');num_rst=$(echo "$a" | sed 's/first//g' | grep -c 'rst'); if [[ $num_rst+1 -gt $num_first ]]; then echo "$a"; fi done &lt; myfile

                    【讨论】:

                      猜你喜欢
                      • 2015-03-25
                      • 2020-08-24
                      • 2019-08-30
                      • 1970-01-01
                      • 1970-01-01
                      • 2011-05-01
                      • 2021-03-01
                      • 1970-01-01
                      • 2017-05-30
                      相关资源
                      最近更新 更多