是否可以从 *每个* grep 匹配中获取最后 N 行上下文?

Is it possible to grab the last N lines of context from *each* grep match?

提问人:merlin2011 提问时间:11/3/2023 最后编辑:Ed Mortonmerlin2011 更新时间:11/8/2023 访问量:59

问:

请考虑以下输入文件:

FixedHeader
Body1
Body2
BodyX
BodyY
BodyZ

Unrelated text111
Unrelated text551
Unrelated text111
Unrelated text551

FixedHeader
Body3
Body4
BodyX
BodyY
BodyZ

Unrelated text111
Unrelated text551

FixedHeader
Body5
Body6
BodyA
BodyB
BodyZC

我可以使用以下命令 grep 获取并拉动它后面的五行:FixedHeader

egrep -A5 "FixedHeader"

但是,我想做的是识别并始终抓住它后面的第 5 行,但不输出上下文的其余任何部分。预期输出:FixedHeader

BodyZ
BodyZ
BodyZC

有没有办法在 grep 匹配的上下文中向下输出 N 行?

我并不特别喜欢 grep,尽管如果存在单行代码,则更可取。

awk grep

评论

1赞 merlin2011 11/8/2023
@EdMorton 我已经按照要求添加了预期的输出。不可以,输入在 FixedHeader 之后可以有任意数量的行。

答:

3赞 merlin2011 11/3/2023 #1

我想出了如何做到这一点:awk

awk '/FixedHeader/ {target_line=NR + 5} NR==target_line' input.log
0赞 Timur Shtatland 11/3/2023 #2

使用这个 Perl 单行代码:

perl -ne '$i = $. + 5 if /FixedHeader/;  print if $. == $i;' in_file

或者,如果模式间隔的频率高于每 5 行,请使用此解决方案(内存效率较低,因为它将整个文件加载到内存中):grep

perl -ne 'push @s, $_; push @i, ( $. - 1 ) if /FixedHeader/; END { @i = map { $i = $_ + 5; $i; } @i; print for @s[@i]; }' in_file

指纹:

BodyZ
BodyZ
BodyZC

Perl 单行代码使用以下命令行标志: :
告诉 Perl 以内联方式查找代码,而不是在文件中查找代码。
:一次循环一行输入,默认分配给输入。
-e-n$_

$.:当前输入行号。

另请参阅:

1赞 Ed Morton 11/8/2023 #3

printing-with-sed-or-awk-a-line-following-a-matching-pattern 上发布的成语样式中,您可以这样做:

$ awk 'c&&!--c; /FixedHeader/{c=5}' file
BodyZ
BodyZ
BodyZC

OP 在他们的答案中的内容对于这个特定问题很好,但上述内容除了更容易适应不同的要求之外并没有更好,但它的工作原理也不是很明显。

但是,或者,由于您的输入中有空行分隔的段落,因此您可以在段落模式下使用 awk,字段由空格分隔,而不是任何空格:\n

$ awk -v RS= -F'\n' '/^FixedHeader/{print $6}' file
BodyZ
BodyZ
BodyZC

与上一个的区别在于,如果打印空行后少于 5 行,而已经讨论过的 2 个解决方案可以从下一段打印出一行,例如:FixedHeader

$ cat file
FixedHeader
Body1
Body2

Unrelated text111
Unrelated text551
Unrelated text111
Unrelated text551

$ awk '/FixedHeader/ {target_line=NR + 5} NR==target_line' file
Unrelated text551
$

$ awk 'c&&!--c; /FixedHeader/{c=5}' file
Unrelated text551
$

$ awk -v RS= -F'\n' '/^FixedHeader/{print $6}' file

$
0赞 Paul Hodges 11/8/2023 #4

嵌套范围也可以解决很多这样的问题。sed

$: sed -n '/^FixedHeader/,+5{         # on a header and the next five lines
             /^FixedHeader/,+4d       # ignore the header and the next FOUR lines
             p                        # print the desired line per header
           }' infile
BodyZ
BodyZ
BodyZC

...但是 Ed 的段落模式解决方案是你更好的选择,因为他给出了原因。awk