полезный ответ msjqu объясняет необходимость экранирования +
символов. как \+
в регулярном выражении для этих символов. трактоваться как литералы .
Таким образом, регулярное выражение для соответствия строке заголовка - 20 +
символов. в начале строки (^
) - это: ^\+{20}
Тем не менее, если достаточно обнаружить строки заголовка по 20 +
знакам, Get-Content -Delimiter
- который поддерживает только литералы в качестве разделителей - предлагает простой и эффективное решение (PSv3 +; предполагается, что входной файл some.log
в текущем каталоге ./
):
$headerPrefix = '+' * 20 # -> '++++++++++++++++++++'
$headerPrefix + (Get-Content ./some.log -Delimiter $headerPrefix -Tail 1)
-Delimiter
использует указанную сигнатуру строки заголовка, чтобы разбить файл на «строки» (текст между экземплярами разделителя, которые здесь составляют блоков строк), а -Tail 1
возвращает последнее » line "(block) путем его поиска из end файла. Кончик шляпы 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
В качестве альтернативы , , если вы хотите принять фиксированное максимальное количество строк в блоке , решение для одного конвейера с использованием 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
, затем выводится строка совпадения, а также все последующие.