如何选择两个相似模式之间的行

3

我有一个文本文件,其中包含这样的文本块:

IN
hit
ER 123 hit 456
abc
hit
ghi
ER 789 hit 012
abc
ghi
IN 345 
abc
def
ghi
ER 678 xxx 901
xyz
hit
xyz
IN
risk
in

区块可以有任意数量的行,但始终以包含 ER 或 IN 的行开头。

使用 awk,如何选择出现在两个类似标记模式之间的行?

1)可能会有多个用这些模式标记的部分。

2)在模式之间选定的行必须包含另一个模式(例如 hit)

3)第一个模式的行(例如 ER)应该被包括在内,第二个模式(例如 ER|IN)的行应该被排除在外。

期望输出:

ER 123 hit 456
abc
hit
ghi
ER 678 xxx 901
xyz
hit
xyz

我已经尝试通过

实现我的目标。
awk '/ER/ {block=1} block {str=str sep $0; sep=RS} /ER|IN/ {block=0; if (str~/hit/) {print str} str=sep=""}'

但它给我
ER abc hit ghi
ER 789 hit 012

编辑:我的例子不够精确。 编辑2:

a)我试图找到与模式“ ER”匹配的行 b)我搜索最接近下一个与模式“ ER”或“ IN”匹配的行 c)我只想在我的结果中打印包含至少一个匹配模式“.hit.”的行,但它不能是第一行。结果应该包括第一行,但不包括最后一行,因此:

ER 678 xxx 901
xyz
hit
xyz

应该打印,因为在匹配“ER”行和匹配“IN”行之间的块中有一行与“hit”匹配。
ER 789 hit 012
abc
ghi

由于在匹配“ER”和匹配“IN”的行之间的块中没有匹配“hit”的行,因此不应打印。

3个回答

2

请尝试以下方法,并告诉我是否能帮到您。

awk '
/ER/ && val{
  if(hit_flag){
    print val};
  val=hit_flag=token=in_er_token=""
}
/ER/ && !val{
  val=$0;
  token=1
  next
}
val && token && (/[Hh][Ii][Tt]/){
  hit_flag=1
}
val && token && (/ER/ || /[Ii][Nn]/){
  if(val){
    in_er_token=1
    };
  next}
!in_er_token{
  val=val?val ORS $0:$0
}
END{
  if(val && hit_flag){
    print val}
}
'   Input_file

抱歉,我没有表达清楚,我已经改变了我的例子。行可能以其他字符开头,但它们必须包含 ER ,所以我需要去掉 "^" ,但是匹配应该在第一行和最后一行之间(不包括它们)。 - Bartosz
@Bartosz,我相信这是所需的正确方法,没有使用“IN”?请在相同的地方让我知道,并通过添加反引号中的代码在评论中添加。 - RavinderSingh13
对于代码块:ER 678 xxx 901 xyz hit xyz IN risk IN,它会输出: ER 678 xxx 901 xyz hit xyz risk - Bartosz
不完全是-带IN的行被跳过,并且打印了带risk的行。它不在ERER | IN行之间。更新示例。 - Bartosz
"risk"这一行仍然在输出。 - Bartosz
显示剩余5条评论

2

使用 GNU awk 和 RT:

$ awk 'BEGIN{RS="(ER|IN)"}NR==1{rt=RT}{ORS=RT}/\nhit/{print (NR==2?rt:"")$0}' file
ER 123 hit 456
abc
hit
ghi
ER 678 xxx 901
xyz
hit
xyz

解释:

$ awk '
BEGIN { RS="(ER|IN)" }      # record separator is ER or IN
NR==1 { rt=RT }             # special handling it hit is in the second record
{ ORS=RT }                  # set matched RS as ORS
/\nhit/ {                   # hit in the record
    print (NR==2?rt:"") $0  # output with special handling for NR==2
}' file

ERINhit的定义可以更加精细化。请注意,在实际需求中实现时需要谨慎。


1
"Awk" 解决方案:
awk '/^(ER|IN)\>/{
         if (f) { if (r ~ /\<hit\>/) print head, r }
         f=1; head=$0; r=""; next
     }
     f{ r=r ORS $0 }' file

输出:

ER 123 hit 456 
abc
hit
ghi
ER 678 hit 901 
xyz
hit
xyz

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