如何从文件中获取两个模式之间最后出现的行?

如何从文件中获取两个模式之间最后出现的行?

我有一个报告进程输出的日志文件,我想从最后一次出现的两个模式之间提取所有行。

这些模式将沿着以下方向:

Summary process started at <datestring>

Summary process finished at <datestring> with return code <num>

整个文件中将存在这些模式的多个实例,以及许多其他信息。我想只打印最后一次出现的情况。

我知道我可以使用:

sed -n '/StartPattern/,/EndPattern/p' FileName

获取图案之间的线条,但不知道如何获取最后的实例。

sed或者awk解决方案就可以了。

编辑: 我根本不清楚当多个StartPatterns出现时没有,或者如果在文件末尾之前EndPattern没有,在检测到.EndPatternStartPattern

  • 对于StartPattern缺少 的多个 s EndPattern,我只想要从最后一个StartPattern到 的行EndPattern
  • 对于没有StartPattern到达 的,我希望一切都达到,然后是过早到达的警告。EOFEndPatternEOFEOF

答案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之间有另一个,或者最后一个没有结尾,或者如果存在与和匹配的行,则可能会给出不同的结果。StartPatternEndPatternStartPatternEndPatternStartPatternEndPattern

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模拟它的实现,如gawkmawk或 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。

相关内容