在模式匹配之前显示 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
匹配应包括3
和4
但不包括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 本身就是模式匹配的主要内容,如果未能解决你的问题,请参考以下文章