删除行:在pattern1之后以及在pattern2和pattern3之间使用awk / sed / perl

时间:2012-06-09 09:48:24

标签: perl sed awk

我需要从文件中删除在pattern1之后在模式2和pattern3 之间的行,如下所示:

aaaaaaaa 
bbbbbbbb
pattern1   <-----After this line
cdededed
ddededed
pattern2
fefefefe   <-----Delete this line
efefefef   <-----Delete this line
pattern3
adsffdsd
huaserew

请您建议如何使用awk或sed或perl来完成此操作。

5 个答案:

答案 0 :(得分:4)

sed '/pattern1/,${ /pattern2/,/pattern3/{/pattern2/b; /pattern3/b; d;} };' file

格式化:

/pattern1/,$ {
    /pattern2/,/pattern3/ {
        /pattern2/b;
        /pattern3/b; 
        d;
    } 
}

说明:

  • /pattern1/,$pattern1之后到文件末尾的行数范围
  • /pattern2/,/pattern3/pattern2pattern3
  • 之间的行范围
  • /pattern2/b;/pattern3/b;会跳过范围内包含的pattern2pattern3行(请参阅the sed faq
  • d删除范围
  • 中的其他行

<强>更新

从评论中,可以重写内部块:

//!d

其中:

  • //(空模式)与最后使用的正则表达式匹配(在本例中为pattern2pattern3
  • !反转下一个命令,使其适用于除匹配模式的行以外的所有
  • d删除这些行

所以完整的重写模式是:

/pattern1/,$ {
    /pattern2/,/pattern3/ {
        //!d
    } 
}

答案 1 :(得分:3)

像状态机一样使用awk:

awk '
    BEGIN {print_line = 1}
    /pattern1/ {consider = 1}
    consider && /pattern2/ {print_line = 0; print}
    consider && /pattern3/ {print_line = 1}
    print_line {print}
' filename

答案 2 :(得分:2)

如果您正在使用perl在命令行上寻找快速解决方案,这是flip-flop运算符的理想情况。现在,有两种方法可以在边缘情况下解释这个问题 - 只要pattern1出现在pattern2之前,这两种方法的功能都相同:

  1. 如果 pattern1 位于 pattern2 之后,但在 pattern3 之前删除 pattern1 pattern3

  2. 或者,如果 pattern1 位于 pattern2 之后,但 pattern3 之前不执行任何操作,除非您看到另一个模式1

  3. 在开始之前,请注意perl争论-p

    -n                assume "while (<>) { ... }" loop around program
    -p                assume loop like -n but print line also, like sed
    

    现在,首先,我给你了..

    perl -pe'$x ||= /7/; $_= "" if /5/ .. /8/ and $x' <(seq 1 10)
    1
    2
    3
    4
    5
    6
    9
    10
    

    $x ||= /7/:当$x/7/时,这会将$x设置为false的返回值。 /7/匹配时会返回true。这意味着$x在第一次匹配时设置为true,||=的性质永远不会在变量已经为真时设置。

    如果范围介于$_ = ''/5/之间并且已将/8/设置为true,则会设置$x。请记住短路的工作方式:a && b表示仅当b评估为a时才会运行true。在这种情况下,仅仅评估a的事实将设置触发器操作符的状态 - 这就是我们想要的;但是,如果已经看到$_ = '',我们只希望7出现。

    现在,对问题的第二种解释只是切换顺序......

    perl -pe'$x ||= /7/; $_= "" if $x and /5/ .. /8/' <(seq 1 10)
    

    这将打印全范围。 Perl在找到/5/之后才会开始寻找/7/。在我们不会发生的顺序范围内。

    顺便说一下,要真正把这些答案中的一些归为耻辱,许多空间都不是必需的......

    perl -pe'$x||=/2/;$_=""if$x&&/5/../8/' # secksey
    

答案 3 :(得分:1)

完成Rosetta Stone:

perl -ne '++$saw_pattern1 if /pattern1/;
          $inside = ($saw_pattern1 && /pattern2/) .. /pattern3/;
          print unless $inside && ($inside > 1 && $inside !~ /E0$/)' \
  input

代码利用了Perl的.. range operator

  

在标量上下文中,..返回一个布尔值。运算符是双稳态的,如触发器,并模拟 sed awk 和各种编辑器的行范围(逗号)运算符。每个..运算符都维护自己的布尔状态,甚至在调用包含它的子例程时也是如此。只要其左操作数为假,它就是假的。一旦左操作数为真,范围运算符将保持为真,直到右操作数为真, AFTER ,范围运算符再次变为假。在下次评估范围运算符之前,它不会变为错误...

     

当操作符处于false状态时,不评估右操作数,并且在操作符处于true状态时不评估左操作数。优先级略低于||&&。返回的值是false的空字符串,或者是true的序列号(以1开头)。为遇到的每个范围重置序列号。范围中的最终序列号附加了字符串E0,它不会影响其数值,但如果要排除端点,则可以搜索。您可以通过等待序列号大于1来排除起始点。

答案 4 :(得分:1)

这可能对您有用:

sed '/pattern1/,$!b;/pattern2/,/pattern3/!b;//!d' file