msjqu 的有用答案解释了转义+
字符的必要性。就像\+
在正则表达式中为了这些字符一样。被视为文字。
因此,匹配标题行的正则表达式 - 20 个+
字符。在行首 ( ^
) - 是:^\+{20}
也就是说,如果通过 20 个+
符号检测标题行就足够了Get-Content -Delimiter
——它仅支持文字作为分隔符——提供了一个简单而有效的解决方案(PSv3+;假设some.log
当前目录中的输入文件./
):
$headerPrefix = '+' * 20 # -> '++++++++++++++++++++'
$headerPrefix + (Get-Content ./some.log -Delimiter $headerPrefix -Tail 1)
-Delimiter
使用指定的标题行签名将文件分成“行”(分隔符实例之间的文本,这里是行块)并通过从文件末尾-Tail 1
搜索返回最后一个“行”(块). 向mjsqu 致敬,帮助我找到了这个解决方案。
以下替代解决方案是基于正则表达式的,它支持更复杂的标题行匹配。
注意:虽然以下解决方案都不需要将日志文件作为一个整体读取到内存中,但它们确实会读取整个文件,而不仅仅是从末尾读取。
我们可以在switch -regex -file
语句中使用它来处理日志文件的所有行,以便收集以最后一个 ^\+{20}
匹配项开头和之后的行;该代码假定输入文件路径./some.log
:
# Process all lines in the log file and
# collect each block's lines along the way in
# array $lastBlockLines, which means that after
# all lines have been processed, $lastBlockLines contains
# the *last* block's lines.
switch -regex -file ./some.log {
'^\+{20}' { $lastBlockLines = @($_) } # start of new block, (re)initialize array
default { $lastBlockLines += $_ } # add line to block
}
# Output the last block's lines.
$lastBlockLines
或者,如果您愿意假设 block 中的最大行数是固定的,则可以使用单管道解决方案Select-String
:
Select-String '^\+{20}' ./some.log -Context 0,100 | Select-Object -Last 1 |
ForEach-Object { $_.Line; $_.Context.PostContext }
Select-String '^\+{20}' ./some.log -Context 0,100
匹配文件中的所有标题行,./some.log
并且由于-Context 0, 100
,在发出的匹配对象中包含(最多)100 行匹配行之后的行(这0
意味着不包含匹配行之前的行)。
Select-Object -Last 1
只通过最后一场比赛。
ForEach-Object { $_.Line; $_.Context.PostContext }
然后输出最后一个匹配的匹配行以及它后面的最多 100 行。
如果您不介意阅读该文件两次,您可以Select-String
结合Get-Content ... | Select-Object -Skip
:
Get-Content ./some.log | Select-Object -Skip (
(Select-String '^\+{20}' ./some.log | Select-Object -Last 1).LineNumber - 1
)
这利用了这样一个事实,即由 发出的匹配对象Select-String
具有.LineNumber
反映找到给定匹配的行号的属性。将最后一个匹配的行号减去 1 以Get-Content ... | Select-Object -Skip
输出匹配的行以及所有后续行。