6

我需要从文件中删除pattern1 之后以及pattern 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 中完成此操作。

4

5 回答 5

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

格式化:

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

解释:

  • /pattern1/,$pattern1是文件末尾之后的行范围
  • /pattern2/,/pattern3/是和之间的线pattern2范围pattern3
  • /pattern2/b;/pattern3/b;跳过原本包含在范围内的pattern2and行(请参阅sed 常见问题解答pattern3
  • d删除范围内的其他行

更新

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

//!d

在哪里:

  • //(一个空模式)匹配最后使用的正则表达式(在这种情况下是pattern2pattern3
  • !反转下一个命令,使其适用于与模式匹配的行之外的所有内容
  • d删除这些行

所以完整的,重写的模式是:

/pattern1/,$ {
    /pattern2/,/pattern3/ {
        //!d
    } 
}
于 2012-06-09T10:12:33.117 回答
4

像状态机一样使用 awk:

awk '
    BEGIN {print_line = 1}
    /pattern1/ {consider = 1}
    consider && /pattern2/ {print_line = 0; print}
    consider && /pattern3/ {print_line = 1}
    print_line {print}
' filename
于 2012-06-09T10:45:18.810 回答
2

如果您正在使用 perl 在命令行上寻找快速解决方案,这对于flip-flop操作员来说是一个理想的情况。现在,有两种方法可以在极端情况下解释这个问题——只要pattern1出现之前,这两种方法的功能都是一样的pattern2

  1. 如果pattern1在 pattern2之后但在pattern3之前,则删除pattern1pattern3之间的所有内容

  2. 或者,如果pattern1在 pattern2之后但在pattern3之前什么都不做除非你看到另一个pattern1

在我们开始之前,请注意 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/when $xis的返回值false。匹配时/7/会返回。true这意味着$x在第一次匹配时设置为 true,并且本质||=是永远不会在变量已经为 true 时设置它。

然后它设置$_ = ''范围是否在和之间/5//8/并且它已经设置$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
于 2012-06-09T14:01:11.447 回答
1

这可能对您有用:

sed '/pattern1/,$!b;/pattern2/,/pattern3/!b;//!d' file
于 2012-06-09T13:15:04.963 回答
1

完成罗塞塔石碑:

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

该代码利用了 Perl 的..范围运算符

在标量上下文中,..返回一个布尔值。该运算符是双稳态的,就像一个触发器,并模拟sedawk和各种编辑器的行范围(逗号)运算符。每个..运算符都维护自己的布尔状态,即使在调用包含它的子例程时也是如此。只要它的左操作数是假的,它就是假的。一旦左操作数为真,范围运算符保持真,直到右操作数为真,之后范围运算符再次变为假。直到下一次评估范围运算符时它才会变为假……</p>

当运算符处于假状态时不计算右操作数,而当运算符处于真状态时不计算左操作数。优先级略低于||&&。返回的值要么是空字符串(表示 false),要么是序列号(以 1 开头)表示 true。对于遇到的每个范围,都会重置序列号。范围中的最终序列号E0附加了字符串,这不会影响其数值,但如果您想排除端点,则可以搜索一些内容。您可以通过等待序列号大于 1 来排除起点。

于 2012-06-09T12:04:21.550 回答