使用 grep 或其他命令 return 多行模式的行号

Using grep or other command to return the line number of a multiline pattern

我正在使用 less 命令浏览一个非常大的文本日志文件 (15 GB) 并试图搜索多行模式,但经过一番调查后,less 命令只能搜索单线图案。

有没有办法使用 grep 或其他命令来 return 多行模式的数字行?

十万次迭代的日志格式是这样的:

Packet A
op_3b       : 001
ctrl_2b     : 01
ini_count   : 5

Packet F
op_3b       : 101
ctrl_2b     : 00
ini_count   : 4

Packet X
op_3b       : 010
ctrl_2b     : 11
ini_count   : 98

Packet CA
op_3b       : 100
ctrl_2b     : 01
ini_count   : 5

Packet LP
op_3b       : 001
ctrl_2b     : 00
ini_count   : 0

Packet ZZ
op_3b       : 111
ctrl_2b     : 01
ini_count   : 545

Packet QEA
op_3b       : 111
ctrl_2b     : 11
ini_count   : 0

我想要得到的是 grep 或其他一些命令 return 当这三行模式出现时行号的开始:

op_3b       : 001
ctrl_2b     : 00
ini_count   : 0

假设模式在文件 pattern 中是这样的:

$ cat pattern
op_3b       : 001
ctrl_2b     : 00
ini_count   : 0

然后,尝试:

$ awk '[=11=] ~ pat' RS=  pat="$(cat pattern)" logfile
Packet LP
op_3b       : 001
ctrl_2b     : 00
ini_count   : 0

工作原理

  • RS=

    这会将记录分隔符 RS 设置为空字符串。这告诉 awk 使用空行作为记录分隔符。

  • pat="$(cat pattern)"

    这告诉 awk 创建一个 awk 变量 pat,其中包含文件 pattern.

    的内容

    如果您的 shell 是 bash,那么此命令的一种稍微更有效的形式是 pat="$(<pattern)"。 (除非你确定你的shell是bash,否则不要使用它。)

  • [=21=] ~ pat

    这告诉 awk 打印任何匹配模式的记录。

    [=22=]是当前记录的内容。 ~ 告诉 awk 在 [=22=] 中的文本和 pat.

    中的正则表达式之间进行匹配

    (如果 pattern 的内容有任何正则表达式活动字符,我们需要将它们转义。您当前的示例没有任何所以这不是问题。)

替代风格

有些人更喜欢用不同的风格来定义 awk 变量:

$ awk -v RS=  -v pat="$(cat pattern)" '[=12=] ~ pat' logfile
Packet LP
op_3b       : 001
ctrl_2b     : 00
ini_count   : 0

这也是一样的。

显示行号

$ awk -F'\n' '[=13=] ~ pat{print "Line Number="n+1; print "Packet" [=13=]} {n=n+NF-1}' RS='Packet'  pat="$(cat pattern)" logfile
Line Number=20
Packet LP
op_3b       : 001
ctrl_2b     : 00
ini_count   : 0

这是我的尝试:

awk -v RS="" -v FS="\n" -v op=001 -v ctrl=00 -v ini=0 '~op&&~ctrl&&~ini' data.txt

到目前为止最好的方法是 John1024 使用 awk 的方法,因为你可以一次性完成,如果你真的想寻求 grep 解决方案,你可以使用:

$ grep -m 1 -zoP 'Packet\s*[^\s]*\s*(?=op_3b\s*:\s*001\s*ctrl_2b\s*:\s*00\sini_count\s*:\s*0)' file
Packet LP

备注:

  • -m 1 将在第一次匹配后进行 grep return,如果您的模式出现多次,您可以将其删除。
  • -z 允许多行模式,因为它启用 ASCII NUL 字符而不是正常的 EOL
  • -o 只显示结果匹配而不是整个文件
  • -P 激活 perl 正则表达式

如果你想要行号:

grep -n -f <(grep -m 1 -zoP 'Packet\s*[^\s]*\s*(?=op_3b\s*:\s*001\s*ctrl_2b\s*:\s*00\sini_count\s*:\s*0)' file) file
21:Packet LP

但是您需要执行 2 次传递,因此对于 15GB 的文件 awk 是最佳方法。

如果您的数据在 'd' 文件中,请尝试:

grep -nEA2 '^op_3b\s*:\s*001' d

编辑上面的数字 001 作为您的关键搜索