在两个模式之间使用 Awk,并且模式在中间

5

您好,我正在寻找一个能够查找两个模式并在它们之间找到数据的awk程序,只有当中间存在第三个模式时才将其打印到文件中。例如:

Start
1
2
middle
3
End
Start
1
2
End

And the output will be:
Start
1
2
middle
3
End

我在网上发现了这样一个命令:awk '/patterns1/, /patterns2/' path > text.txt,但是我只需要输出中间带有第三个模式的内容。

有些麻烦,但是可行。你需要保存开始和结束之间的材料,并且当你遇到中间时,注意保存的材料应该被打印出来,当你处理结束时,看看是否应该打印保存的材料。我现在没有时间将其简化为代码。(在识别到开始后,将每个$0保存在一个数组中;在结束时停止保存,如果适当则打印数组并清除数组。) - Jonathan Leffler
还有,数据行是否可能不在“开始”和“结束”之间?或者它总是一个“开始”到“结束”的序列,但只需要打印其中的一些。 - Jonathan Leffler
可能为空,但我只需要中间模式的那个。 - Ggdw
那么文件可能包含:Start1middle2EndJunk<blank line>Start34End?而Junk<blank line>不应该包含在输出中?只有前5行应该被回显? - Jonathan Leffler
提供中间部分,起始和结束处可能包含垃圾或空行 - Ggdw
显示剩余2条评论
5个回答

4

以下是一种不需要标志的解决方案:

$ awk 'BEGIN{RS="End"}/middle/{printf "%s", $0; print RT}'  file
Start
1
2
middle
3
End

解释:变量RS是记录分隔符,因此我们将其设置为“End”,以便每个记录都由“End”分隔。
然后,我们使用/middle/过滤器过滤包含“middle”的记录,并对匹配的记录打印当前记录和分隔符print RT

有趣...但我认为需要解释一下它的工作原理。 - Jonathan Leffler
1
这并没有考虑 Start,它只打印包含 middleEnd 的记录。你还在记录后添加了一个额外的换行符。 - Chris Seymour
@JonathanLeffler添加了一个解释。 - user000001
2
请看我的附加问题,在问题级别的评论中对OP提问。如果除了Start..End序列之外没有其他数据,这种方法效果很好;但如果还需要删除其他数据,则效果不佳。如果数据允许这种方法工作,那么这是一个好主意。 - Jonathan Leffler

3
这个 awk 应该可以工作:
awk '$1=="Start"{ok++} ok>0{a[b++]=$0} $1=="middle"{ok++} $1=="End"{if(ok>1) for(i=0; i<length(a); i++) print a[i]; ok=0;b=0;delete a}' file

Start
1
2
middle
3
End

扩展:

awk '$1 == "Start" {
   ok++
}
ok > 0 {
   a[b++] = $0
}
$1 == "middle" {
   ok++
}
$1 == "End" {
   if (ok > 1)
      for (i=0; i<length(a); i++)
         print a[i];
   ok=0;
   b=0;
   delete a
}' file

3

只需在 awk 中使用一些标志:

/Start/ {
    start_flag=1
}

/middle/ {
    mid_flag=1
}

start_flag {
    n=NR;
    lines[NR]=$0
}

/End/ {
    if (start_flag && mid_flag)
        for(i=n;i<NR;i++)
            print lines[i]
    start_flag=mid_flag=0
    delete lines
}

3

修改了 awk 用户000001。

awk '/middle/{printf "%s%s\n",$0,RT}' RS="End" file

编辑: 添加了开始标签测试

awk '/Start/ && /middle/{printf "%s%s\n",$0,RT}' RS="End" file

这个没有检查 Start - mschilli
更新了我的帖子以测试“开始”。 - Jotne

2

这适用于任何现代的awk:

awk '/Start/{f=1;rec=""} f{rec=rec $0 ORS} /End/{if (rec~/middle/) printf "%s",rec}' file

设置RS为“End”的解决方案是gawk特有的,这可能没问题,但值得一提。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接