在两个关键行之间查找一段文本的grep命令

7

我有一个文本文件,其中包含大致格式如下的文本块:

Beginning of block
...
...
...
.........some_pattern.......
...
...
End of block

Beginning of block
...
... etc.

这些块可以有任意数量的行,但始终以两个分隔符开头。我想要做的是匹配“some_pattern”并将整个块打印到标准输出。使用上面的示例,我只会得到以下输出:

Beginning of block
...
...
...
.........some_pattern.......
...
...
End of block

我尝试了类似以下的方法,但是没有成功:

grep "Beginning of block\n.*some_pattern.*\n.*End of block"

您知道如何使用grep(或其他工具)实现这一点吗?
5个回答

10

我想这里用awk更好:

awk '/Beginning of block/ {p=1};
     {if (p==1) {a[NR]=$0}};
     /some_pattern/ {f=1};
     /End of block/ {p=0; if (f==1) {for (i in a) print a[i]};f=0; delete a}' file

解释

只有在 p 标志处于“激活”状态并且匹配了 some_pattern 时,它才会打印:

  • 当它找到 Beginning of block 时,它会将变量 p=1 并开始将行存储在数组 a[] 中。
  • 如果它找到了 some_pattern,它会将标志 f 设置为 1,以便我们知道已经找到了该模式。
  • 当它找到 End of block 时,它会重置 p=0。如果自上一个 Beginning of block 以来找到了 some_pattern,则会打印所有已存储的行。最后,清除 a[] 并重置 f;当我们再次遇到 Beginning of block 时,我们将有一个全新的开端。

其他测试

$ cat a
Beginning of block
blabla
.........some_pattern.......
and here i am
hello
End of block

Beginning of block
...
... etc.
End of block
$ awk '/Beginning of block/ {p=1}; {if(p==1){a[NR]=$0}}; /some_pattern/ {f=1}; /End of block/ {p=0; if (f==1) {for (i in a) print a[i]}; delete a;f=0}' a
Beginning of block
blabla
.........some_pattern.......
and here i am
hello
End of block

谢谢,但我该如何仅检索包含“some_pattern”的块?我已更新我的问题以显示我正在寻找的输出。有什么想法吗? - laurent
嗯,这更加复杂。对于这种情况,您可以将行保存在数组中,然后设置一个“some_pattern”的标志,最后打印它是否存在。我一会儿会尝试一下。 - fedorqui
@Laurent刚刚更新了这个需求的正确解决方案。 - fedorqui
@Dining Philosopher 非常感谢您的编辑! 感谢您修复了错误并改进了解释 :) - fedorqui

7
以下内容可能适合您:
sed -n '/Beginning of block/!b;:a;/End of block/!{$!{N;ba}};{/some_pattern/p}' filename

3

不确定我是否错过了什么,但这是上面一个答案的简化变体:

awk '/Beginning of block/ {p=1}; 
     /End of block/ {p=0; print $0}; 
     {if (p==1) print $0}'

End of Block这种情况下,您需要打印输入行以获取两个分隔符。

我想要一种轻微的变化,不打印分隔符。在OP的问题中,分隔符模式是简单且唯一的。那么最简单的方法就是使用管道| grep -v block。我的情况比较不规则,因此我使用了以下变化。请注意next语句,这样第三个语句不会打印开放块:

awk '/Beginning of block/ {p=1; next}; 
     /End of block/ {p=0}; 
     {if (p==1) print $0}'

1
正是我所需要的。谢谢! - Brandt

1
这是一种使用awk的方法:


awk '/Beginning of block/ { r=""; f=1 } f { r = (r ? r ORS : "") $0 } /End of block/ { if (f && r ~ /some_pattern/) print r; f=0 }' file

结果:

Beginning of block
...
...
...
.........some_pattern.......
...
...
End of block

0
sed -n "
/Beginning of block/,/End of block/ {
   N
   /End of block/ { 
      s/some_pattern/&/p
      }
   }"

对于这种处理,sed 是非常高效的。

使用 grep 时,你肯定需要通过中间文件或数组。


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