grep跳过文件的n行,仅在之后搜索


9

我有一个很大的日志文件,想grep第一次出现某个模式,然后在出现这种情况后立即找到另一个模式。

例如:

123
XXY
214
ABC
182
558
ABC
856
ABC

在我的示例中,我想查找182然后找到下一个出现的ABC

第一次出现很简单:

grep -n -m1 "182" /var/log/file

输出:

5:182

我如何找到下一次出现的ABC?

我的想法是根据行号182 告诉grep跳过第一n行(在上面的示例中n=5)。但是我该怎么做?


1
是否grep使用了要求?我不认为这可以做到,grep但是使用awksed(单独使用或与结合使用grep)会很容易。
Hauke Laging 2015年

不需要@HaukeLaging grep。我还不熟悉sedor awk。如果您有一个好的解决方案,请让我听听!:) @don_crissti只应打印第一行。我不在乎其他情况。
koljanep 2015年

Answers:


10

通过使用,sed您可以q一次完成范围和uit输入:

sed '/^182$/p;//,/^ABC$/!d;/^ABC$/!d;q'

同样,使用GNU,grep您可以在两个greps 之间分割输入:

{ grep -nxF -m1 182; grep -nxF -m1 ABC; } <<\IN
123
XXY
214
ABC
182
558
ABC
856
ABC
IN

...打印...

5:182
2:ABC

...表示第一个grep发现一个-F固定字符串文字,-x整行182从其读取开始匹配了5行,第二个发现了类似类型的ABC从其读取开始匹配了2行-或2行第5行的第一次grep 退出阅读之后

来自man grep

-m NUM, --max-count=NUM
          Stop  reading  a  file  after  NUM  matching
          lines.   If the input is standard input from
          a regular file, and NUM matching  lines  are
          output, grep ensures that the standard input
          is  positioned  to  just  after   the   last
          matching  line before exiting, regardless of
          the  presence  of  trailing  context  lines.
          This  enables  a calling process to resume a
          search. 

为了进行可重复的演示,我使用了here-document,但是您可能应该这样做:

{ grep ...; grep ...; } </path/to/log.file

它还可以与其他shell复合命令构造一起使用,例如:

for p in 182 ABC; do grep -nxFm1 "$p"; done </path/to/log.file

+1在手册页中看到了。这就是我尝试过的方法,只是在grep' 之间加了一个管道,而不是;...禁止
Xen2050 2015年

@ Xen2050-管道通常不起作用-共享输入时通常需要一个可查找文件。
mikeserv

令人印象深刻的答案,但我不支持您关于管道的声明。两者grep共享的here文档实际上是他们的管道。其他问题:我尝试不打印标记线,但sed '//,/^ABC$/!d;/^ABC$/!d;q'抛出一个奇怪的错误。怎么//办?
Hauke Laging 2015年

1
@HaukeLaging-这里文档(在大多数shell中)不是管道-它是由shell创建的真实tmp文件,在执行任何写操作之前,shell会删除该shell-同时维护描述符。它仍然是可寻求的。通常,管道是不可吸引的。我会看的sed东西-很快就写出来了。
mikeserv 2015年

1
@HaukeLaging-哦,这sed起作用了-您刚刚删除了引用。输入中,sed您可以/address/使用空//地址再次引用最后一个。所以/^182$/command;//,/next_address/少了点/^182$/command;/^182$/,/next_address/。如果您使用的是GNU,错误可能不是以前的正则表达式sed。顺便说一句,可以通过/dev/fd/[num]Linux系统上的链接通过间接操作来操纵管道-但是,如果您不太谨慎地处理缓冲区(例如使用dd,通常会是一场失败的战斗。
mikeserv

2

使用grep与Perl兼容的正则表达式(pcregrep):

pcregrep -Mo '182(.|\n)*?\KABC'

选项-M允许模式匹配多于一行,并且\K不包括匹配的模式(至此为止)到输出中。\K如果您想要拥有整个区域,则可以删除。


2
> awk '/^182$/ { startline=1; }; startline == 0 { next; }; /^ABC$/ { print "line " NR ": " $0; exit; }' file
line 7: ABC

1
随处都有第一个ABC ; 这个问题想要第一个182 之后的第一个ABC 。最直接的标志是awk '/^182$/{z=1;next} z&&/^ABC$/{print NR":"$0;exit}' file-,或者您可以编写至少一个getline()通常更笨拙的显式循环,或者使用类似于@JRFerguson的perl的范围来变得聪明(?):awk '!x&&/^182$/,/^ABC$/ {x=NR":"$0} END{print x}
dave_thompson_085

@ dave_thompson_085的确如此。正确的想法,但经过严格编码(在编写过程中混合了两个想法)。令人尴尬的是,我什至尝试了一下,但并不奇怪输出的结果。
Hauke Laging 2015年

1

您可以使用的Perl版本是:

perl -nle 'm/182/../ABC/ and print' file

...在匹配范围内打印行。

如果文件包含多个匹配范围,则可以通过将/定界符更改为来将输出限制为仅第一个范围?

perl -nle 'm?182?..?ABC? and print'

1

坚持正义grep并添加tailcut,您可以...

grep表示第一个匹配项的行号182

grep -m 1 -n 182 /var/log/file |cut -f1 -d:

ABC仅在上面的第一行匹配后,使用that来为所有s 进行grep,在第K行后使用tails -n +K进行输出。全部一起:

tail -n +$(grep -m 1 -n 182 /var/log/file |cut -f1 -d:) /var/log/file | grep ABC

-m 1再次添加以仅找到第一个匹配项ABC

tail -n +$(grep -m 1 -n 182 /var/log/file|cut -f1 -d:) /var/log/file|grep -m 1 ABC

参考:
man页面
/programming/6958841/use-grep-to-report-back-only-line-numbers


1

另一个变体是:

grep -n -A99999 "182" /var/log/file|grep -n -m1 "ABC"

标记-An在比赛和99999之后n行时只是为了确保我们不会错过任何比赛。较大的文件应具有更多行(请使用“ wc -l”检查)。


0

范围运算符,可以在这里使用:

< yourfile \
sed -e '
   /182/,/ABC/!d
   //!d;=;/ABC/q
' | sed -e 'N;s/\n/:/'

范围运算符..与仅匹配一次运算符m??可以在此处使用Perl

perl -lne 'm?182? .. m?ABC? and print "$.:$_" if /182/ || /ABC/' yourfile
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.