【问题标题】:Print all lines between two patterns, exclusive, first instance only (in sed, AWK or Perl) [duplicate]打印两个模式之间的所有行,独占,仅第一个实例(在 sed、AWK 或 Perl 中)[重复]
【发布时间】:2019-08-08 18:22:15
【问题描述】:

使用 sed、AWK(或 Perl),如何打印两个模式(第一个实例)之间的所有行,不包括模式?1

即作为输入给出:

aaa
PATTERN1
bbb
ccc
ddd
PATTERN2
eee

甚至可能:

aaa
PATTERN1
bbb
ccc
ddd
PATTERN2
eee
fff
PATTERN1
ggg
hhh
iii
PATTERN2
jjj

我希望,在这两种情况下:

bbb
ccc
ddd

1许多用户投票结束此问题,因为该问题与this 一个重复。最后,我提供了一个gist,证明它们是不同的。这个问题表面上也类似于anumberofothers,但是没有完全匹配,而且没有一个是高质量的,而且,我相信这个具体问题是最常见的面对,它应该有一个清晰的表述,以及一套正确、清晰的答案。

【问题讨论】:

  • 关于此的元数据:Can I create this new question or will it be closed as a dupe or otherwise cause controversy?。很奇怪,它没有被标记为How to select lines between two patterns? 的重复项。正如在那篇文章中提到的,这个想法是编译一组选项,为此它被标记为 CW。你说这不是骗人的,因为一个答案不包括一个案例。再写一篇规范对我来说似乎是在浪费时间,而且会导致知识分散。
  • 嗯@hek 我在这里留下了我的评论,然后与 Alex、tripee 和我进行了一些有趣的辩论。我现在就把它打开,看看是否能吸引观众。无论如何,我看到我们在不同的地方(还有 Meta)异步地讨论这个话题,所以很难达成共识。
  • @fedorqui 我以某种方式在这里和那里进行了讨论。对我来说,这与您的问题明显重复-这是我的第一个想法,而不受您上面链接的元帖子的影响。我看不出 OP 不应该接受这一点的任何充分理由。

标签: bash perl awk sed


【解决方案1】:

如果您有 GNU sed(在 Mac OS X 上使用 4.7 版进行测试),最简单的解决方案可能是:

sed '0,/PATTERN1/d;/PATTERN2/Q'

解释:

  • d 命令从第 1 行删除到与 /PATTERN1/ 匹配的行。
  • Q 命令然后退出,而不在匹配/PATTERN2/ 的第一行打印。

如果文件只有一个模式实例,或者如果您不介意提取所有这些实例,并且您想要一个不依赖 GNU 扩展的解决方案,则此方法可行:

sed -n '/PATTERN1/,/PATTERN2/{//!p}'

解释:

  • 请注意,空的正则表达式 // 会重复最后一次正则表达式匹配。

【讨论】:

  • 请注意,这只会首先打印模式之间的此类行序列,如果这是您的意图,请将该信息添加到问题中,并且标记的重复问题将不再存在
  • 对不起@Sundeep,我相信我已经说过了,但我现在已经说得更清楚了。
【解决方案2】:

使用awk(假设PATTERN1PATTERN2 始终成对出现,并且它们中的任何一个都不会出现在一对内)

$ cat ip.txt
aaa
PATTERN1
bbb
ccc
ddd
PATTERN2
eee
fff
PATTERN1
ggg
hhh
iii
PATTERN2
jjj

$ awk '/PATTERN2/{exit} f; /PATTERN1/{f=1}' ip.txt
bbb
ccc
ddd
  • /PATTERN1/{f=1} 如果/PATTERN1/ 匹配,则设置标志
  • /PATTERN2/{exit} 如果匹配到/PATTERN2/,则退出
  • f; 如果设置了标志,则打印输入行


通用解决方案,可以指定所需的块

$ awk -v b=1 '/PATTERN2/ && c==b{exit} c==b; /PATTERN1/{c++}' ip.txt
bbb
ccc
ddd
$ awk -v b=2 '/PATTERN2/ && c==b{exit} c==b; /PATTERN1/{c++}' ip.txt
2
46

【讨论】:

  • 建议聊天使用awk '/PATTERN1/{f=1;next}/PATTERN2/{exit}f',我注意到它与awk '/PATTERN2/{exit} f; /PATTERN1/{f=1}'基本相同,这就是为什么我不会将其添加为单独的答案。
【解决方案3】:

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

sed -n '/PATTERN1/{:a;n;/PATTERN2/q;p;$!ba}' file

这只会打印第一组分隔符之间的行,或者如果第二个分隔符不存在,则打印到文件末尾。

【讨论】:

    【解决方案4】:

    我尝试了两次回答,但问题切换为保持/重复状态..

    从@Sundeep 借用输入并添加我在问题 cmets 中分享的答案。

    使用 awk

    awk -v x=0 -v y=1 ' /PATTERN1/&&y { x=1;next } /PATTERN2/&&y { x=0;y=0; next } x ' file
    

    使用 Perl

    perl -0777 -ne ' while( /PATTERN1.*?\n(.+?)^[^\n]*?PATTERN2/msg ) { print $1 if $x++ <1 } '
    

    结果:

    $ cat ip.txt
    aaa
    PATTERN1
    bbb
    ccc
    ddd
    PATTERN2
    eee
    PATTERN1
    2
    46
    PATTERN2
    xyz
    
    $
    
    $ awk -v x=0 -v y=1 ' /PATTERN1/&&y { x=1;next } /PATTERN2/&&y { x=0;y=0; next } x ' ip.txt
    bbb
    ccc
    ddd
    
    $ perl -0777 -ne ' while( /PATTERN1.*?\n(.+?)^[^\n]*?PATTERN2/msg ) { print $1 if $x++ <1 } ' ip.txt
    bbb
    ccc
    ddd
    
    $
    

    使其通用

    awk 这里 y 是输入

    awk -v x=0 -v y=2 ' /PATTERN1/ { x++;next } /PATTERN2/ { if(x==y) exit } x==y ' ip.txt
    2
    46
    

    perl 检查 ++$x 是否发生..这里是 2

    perl -0777 -ne ' while( /PATTERN1.*?\n(.+?)^[^\n]*?PATTERN2/msg ) { print $1 if ++$x==2 } ' ip.txt
    2
    46
    

    【讨论】:

      【解决方案5】:

      添加更多解决方案(可能的方式在这里,为了好玩 :) 而不是声称这些比通常的更好)所有测试和编写在 GNU awk。也仅使用给定的示例进行了测试。

      第一种解决方案:

      awk -v RS="" -v FS="PATTERN2" -v ORS="" '$1 ~ /\nPATTERN1\n/{sub(/.*PATTERN1\n/,"",$1);print $1}' Input_file
      

      第二个解决方案:

      awk -v RS="" -v ORS="" 'match($0,/PATTERN1[^(PATTERN2)]*/){val=substr($0,RSTART,RLENGTH);gsub(/^PATTERN1\n|^$\n/,"",val);print val}' Input_file
      

      第三种解决方案:

      awk -v RS="" -v OFS="\n" -v ORS="" 'sub(/PATTERN2.*/,"") && sub(/.*PATTERN1/,"PATTERN1"){$1=$1;sub(/^PATTERN1\n/,"")} 1' Input_file
      

      以上所有代码输出如下。

      bbb
      ccc
      ddd
      

      【讨论】:

        【解决方案6】:

        使用 GNU sed:

        sed -nE '/PATTERN1/{:s n;/PATTERN2/q;p;bs}'
        

        -n 将修剪除 PATTERN1 和 PATTERN2 之间的所有行,包括两者,因为会有 p 打印输出命令。 每个 sed 范围检查是否为真将只执行下一个,因此 {} 分组是强制性的.. 通过 n 命令删除 PATTERN1(表示下一个),如果到达第一个 PATTERN2 则完全退出,否则打印该行然后在该边界内继续下一行。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-03-12
          • 2015-05-07
          • 2022-01-09
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多