我有一个报告进程输出的日志文件,我想从最后一次出现的两个模式之间提取所有行。
这些模式将沿着以下方向:
Summary process started at <datestring>
和
Summary process finished at <datestring> with return code <num>
整个文件中将存在这些模式的多个实例,以及许多其他信息。我想只打印最后一次出现的情况。
我知道我可以使用:
sed -n '/StartPattern/,/EndPattern/p' FileName
获取图案之间的线条,但不知道如何获取最后的实例。
sed
或者awk
解决方案就可以了。
编辑:
我根本不清楚当多个StartPatterns
出现时没有,或者如果在文件末尾之前EndPattern
没有,在检测到.EndPattern
StartPattern
- 对于
StartPattern
缺少 的多个 sEndPattern
,我只想要从最后一个StartPattern
到 的行EndPattern
。 - 对于没有
StartPattern
到达 的,我希望一切都达到,然后是过早到达的警告。EOF
EndPattern
EOF
EOF
答案1
你总是可以这样做:
tac < fileName | sed '/EndPattern/,$!d;/StartPattern/q' | tac
如果您的系统没有 GNU tac
,您也许可以使用它tail -r
。
你也可以这样做:
awk '
inside {
text = text $0 RS
if (/EndPattern/) inside=0
next
}
/StartPattern/ {
inside = 1
text = $0 RS
}
END {printf "%s", text}' < filename
但这意味着读取整个文件。
请注意,如果 a和下一个StartPattern
之间有另一个,或者最后一个没有结尾,或者如果存在与和匹配的行,则可能会给出不同的结果。StartPattern
EndPattern
StartPattern
EndPattern
StartPattern
EndPattern
awk '
/StartPattern/ {
inside = 1
text = ""
}
inside {text = text $0 RS}
/EndPattern/ {inside = 0}
END {printf "%s", text}' < filename
会使其行为更像该tac+sed+tac
方法(除了未封闭的尾随StartPattern
情况)。
最后一项似乎最接近您编辑的要求。添加警告只需:
awk '
/StartPattern/ {
inside = 1
text = ""
}
inside {text = text $0 RS}
/EndPattern/ {inside = 0}
END {
printf "%s", text
if (inside)
print "Warning: EOF reached without seeing the end pattern" > "/dev/stderr"
}' < filename
为避免读取整个文件:
tac < filename | awk '
/StartPattern/ {
printf "%s", $0 RS text
if (!inside)
print "Warning: EOF reached without seeing the end pattern" > "/dev/stderr"
exit
}
/EndPattern/ {inside = 1; text = ""}
{text = $0 RS text}'
可移植性说明:对于/dev/stderr
,您需要一个具有此类特殊文件的系统(请注意,在 Linux 上,如果在可查找文件上打开 stderr,该文件将在文件开头而不是文件中的当前位置写入文本)或一个awk
模拟它的实现,如gawk
、mawk
或 busybox awk
(这些解决了上面提到的 Linux 问题)。
在其他系统上,您可以替换print ... > "/dev/stderr"
为print ... | "cat>&2"
.
答案2
sed
你可以像这样使用GNU
sed '/START/{:1;$!{/END/!{N;b1};h}};${x;p};d' file
只需覆盖完整多行模式每次出现的保留空间。将其打印在文件末尾。
这将提供一致的行为,例如
- START 和 END 都在同一行,将匹配行。
- 初始 START 之后的多个 START 将匹配所有直到 END
- 如果没有 END,则不会打印匹配项,将打印最后一次出现的完整 START 到 END
答案3
对于GNU sed
,另一个解决方案可能是(使用变量P1
/P2
作为开始/结束模式):
sed -n "/${P1}/,/${P2}/H; /${P1}/h; \${g;p}"
与@Stéphane Chazelas 解决方案的主要区别在于:
- 如果最后一个 END/EOF 之前有多个 START,我们将显示从最后一个 START 到最后一个 END/EOF。
- 与 START 位于同一行的任何 END 都会被忽略
- 支持最后一个输入行中的最后一个 END
- 如果最后一个 START 之后没有 END,我们从最后一个 START 打印到 EOF
答案4
这是 awk 的解决方案:
awk '/EndPattern/ {recording=0} recording>0 {buffer=buffer $0 "\n"} /StartPattern/ {recording+=1; buffer=""} END {printf "%s", buffer; if(recording>0) {print "WARNING: missing EndPattern" > "/dev/stderr"}}'
因此,对于以下输入:
1
StartPattern
2
3
EndPattern
4
5
StartPattern
6
7
EndPattern
8
您将得到以下输出:
6
7
如果您想要精确的行匹配,请将 StartPattern 替换为 ^StartPattern$,EndPattern 也是如此。如果您想忽略嵌套模式,还可以将recording+=1 替换为recording=1。