在模式匹配之前显示 n 行和在模式匹配之后显示 m 行 n & m 本身就是模式匹配

Posted

技术标签:

【中文标题】在模式匹配之前显示 n 行和在模式匹配之后显示 m 行 n & m 本身就是模式匹配【英文标题】:Display n lines before and m lines after a pattern match were n & m are themselves pattern matches 【发布时间】:2015-04-02 23:31:56 【问题描述】:

我有这样的数据:

foo
...
bar
...
pattern
...
]

我需要首先匹配“模式”,然后显示“模式”之前的所有内容到“foo”以及模式之后的所有内容到“]”

grep 应该这样做:

grep pattern -A grep foo -B grep ]

可惜没有。

答案不需要包含 grep。 欢迎使用 awk、sed 等。

【问题讨论】:

那么您正在寻找的确切输出是什么? 您的样本输入不足。显示示例输入,其中包括范围重叠和/或文件开始/结束发生在预期范围之前/之后等情况以及相关输出。还要定义“模式”(是 stginr、BRE、ERE 还是其他?)并显示您是否希望整个“单词”或整行仅匹配或部分匹配。否则,我们只是猜测您的几乎所有要求。 【参考方案1】:

Soo...如果包含与pattern 匹配的内容,您想在匹配foo 的内容和匹配] 的内容之间打印一个部分,对吗?那么

sed -n '/foo/  :a; N; /\]/!ba /pattern/ p ' filename

sed 代码的工作原理如下:

/foo/        # if a line matches foo
  :a          # jump label
  N           # fetch the next line and append it to the pattern space
  /\]/! ba    # if the result does not match ] (that is, if the last fetched
              # line does not contain something that matches ]), go back to :a
  /pattern/ p # if in all these lines, there is something that matches the
              # pattern, print them

使匹配在前面不贪心——也就是说,如果在一个文件中

1
foo
2
foo
3
pattern
4
]
5

匹配应包括34 但不包括2,脚本可以这样修改(或类似,取决于您要使用的模式):

sed -n '/foo/  :a; N; /\n[^\n]*foo/ s/.*\n//; /\]/!ba /pattern/ p ' filename

如果该行中的某些内容与 foo 匹配,/\n[^\n]*foo/ s/.*\n// 将删除最后获取行之前的所有内容。

如果您的图案是线条图案(即,如果它们包含^$),则需要对其进行修改。一旦模式空间中存在多行,^ 将匹配模式空间的开头和$ 的结尾,而不是一行。然后,您可以使用 \n 来匹配行尾。例如,如果您想在精确为 foo] 的行之间进行非贪婪匹配(如果它们之间存在精确为 pattern 的行),您可以使用

sed -n '/^foo$/  :a; N; /\nfoo$/ s/.*\n//; /\n\]$/!ba /\npattern\n/ p ' filename

【讨论】:

【参考方案2】:

这是awk

awk '/foo/ t=1 t a[++b]=$0 /pattern/ f=1 /^]/ if (f) for (i=1;i<=b;i++) print a[i];delete a;b=t=f=0' file

示例数据

cat file
foo
data
more
]
foo
...
bar
...
pattern
...
]
more
foo
here
yes
]
end

awk测试

awk '/foo/ t=1 t a[++b]=$0 /pattern/ f=1 /^]/ if (f) for (i=1;i<=b;i++) print a[i];delete a;b=t=f=0'
foo
...
bar
...
pattern
...
]

一些更容易阅读:

awk '
/foo/ t=1 
t a[++b]=$0 
/pattern/ f=1 
/^]/ if (f) 
    for (i=1;i<=b;i++) 
        print a[i]
    delete a
    b=t=f=0
    
'

测试是否找到foo,将t设置为true 如果t 为真,则将所有行存储在数组a 如果找到pattern,则设置标志f 如果找到],则测试标志f是否为真,然后打印数组a 重置所有内容并重新开始。

【讨论】:

【参考方案3】:

使用 perl 的单行代码:

perl -wln -0777 -e 'm/foo((?!foo).)*pattern[^\]]*\]/s and print $&;' [filename]

输入:

foo
foo
...
bar
...
pern
...
]
]
foo
... 
pattern
]
]
foo
]

输出:

perl -wln -0777 -e 'm/foo((?!foo).)*pattern[^\]]*\]/s and print $&;' testtest
foo
... 
pattern
]

analysis on regex101

一些关键点:

    在 perl 中使用m/.../s 开启单行模式参考this post: 正则表达式foo((?!foo).)*pattern[^\]]*\] foo匹配第一个foo ((?!foo).)* 避免在匹配部分使用negative lookahead 匹配foo pattern 匹配模式 [^\]]*\] 以下部分不应包含 ] 并以 ] 结尾

【讨论】:

以上是关于在模式匹配之前显示 n 行和在模式匹配之后显示 m 行 n & m 本身就是模式匹配的主要内容,如果未能解决你的问题,请参考以下文章

grep正则表达式

匹配列表中的一个元素,然后返回它之前的“n”个元素和它之后的“m”个元素

18文本处理工具-grep

linux学习--grep与sed

sed 命令操作

Linux三剑客入门