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

Display n lines before and m lines after a pattern match were n & m are themselves pattern matches

我有这样的数据:

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

我需要先匹配到 'pattern' 然后显示 'pattern' 之前的所有内容直到 'foo' 以及模式之后的所有内容到 ']'

grep 应该这样做:

grep pattern -A grep foo -B grep ]

可惜不是。

答案不需要包含 grep。 欢迎 awk、sed 和其他人。

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

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

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

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

这是一个awk

awk '/foo/ {t=1} t {a[++b]=[=10=]} /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]=[=12=]} /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]=[=13=]} 
/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 重置所有提示并重新开始。

单行使用 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

一些要点:

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