Вопрос:
Я думаю, это должно быть просто. Я пишу вывод журнала xcopy в текстовый файл с ежедневным ограничителем (буквально) «++++++++++++++++++++Tue 07/03/2018 0900 PM» прикрепленный к файлу журнала до каждого ежедневного резервного копирования. Таким образом, последние строки в файле обычно выглядят следующим образом:
Новый день добавляет новую строку разделителя и так далее.
Я хочу отобразить разделитель LAST и строки, которые следуют за eof.
Схема, которую я пробовал GET-Content, Select-String -Context 0,20 не работает,
PS говорит, что моя строка поиска +++++++++++++ не является регулярным выражением, не распознает путь и т.д. Любая помощь?
Память и время не под вопросом. Извините, если это слишком просто.
Лучший ответ:
msjquew полезный ответ объясняет необходимость выхода + символов. как + в регулярном выражении для этих символов. которые рассматриваются как литералы.
Таким образом, регулярное выражение соответствует строке заголовка – 20 + символов. в начале строки (^) – есть: ^+{20}
При этом, если достаточно обнаружить строки заголовка Get-Content -Delimiter 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 lines along the way in # array $lastBlockLines, which means that after # all lines have been processed, $lastBlockLines contains # the *last* block 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 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 } ForEach-Object { $_.Line; $_.Context.PostContext } выводит последнюю строку соответствия соответствия, а также до 100 строк, которые следуют за ней.
Если вы не прочь прочитать файл дважды, вы можете комбинировать Select-String с Get-Content… | Select-Object -Skip 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 Get-Content… | Select-Object -Skip затем выводит соответствующую строку, а также все последующие.
Ответ №1
TL;DR; Побег + в вашем поиске, используйте “ +++” и т.д.
Фон
К сожалению, + является зарезервированным персонажем в мире регулярных выражений.
В чем смысл + в регулярном выражении?
Он сообщает двигателю, что он соответствует предыдущему оператору поиска (либо символу, диапазону, либо коду, представляющему группу символов, таких как d – цифры) один или несколько раз. Вы можете увидеть дополнительную информацию об этой ошибке в Powershell, выполнив следующее:
[regex]$x = «++++»
Возвращает:
Cannot convert value «++++» to type «System.Text.RegularExpressions.Regex». Error: «parsing «++++» — Quantifier {x,y} following nothing.» At line:1 char:1 + [regex]$x = «++++» + ~~~~~~~~~~~~~~~~~~ + CategoryInfo : MetadataError: (:) [], ArgumentTransformationMetadataException + FullyQualifiedErrorId : RuntimeException
Он говорит, что квантификатор (+) ничего не значит.
Поэтому нам нужно избежать использования +:
[regex]$x = «++++» $x.Match(‘++++’)
Возвращая следующее, совпадение с ошибкой:
Groups : {0} Success : True Name : 0 Captures : {0} Index : 0 Length : 4 Value : ++++
улучшение
Если вы знаете, сколько + есть, вы можете сопоставить «+{20}», если их осталось 20. Или из предыдущего примера:
[regex]$x = «+{4}» $x.Match(‘++++’) Ответ №2
Другой способ использования RegEx – разделить файл на разделы.
- используйте Get-Content с параметром -Raw чтобы иметь одну строку, а не массив строк
- используйте непрозрачный положительный результат, чтобы разделить файл на разделы, начиная с
20 * + -split ‘(?=+{20})’, которые не пусты -ne » - используйте индекс [-1] чтобы получить последний раздел.
Образец вывода
PS> ((Get-Content ‘.LogFile.txt’ -raw) -split ‘(?=+{20})’ -ne »)[-1] ++++++++++++++++++++Mon 07/03/2018 0900 PM 0 Files(s) copied Xcopy SUCCEEDED K: to J:MyUSBBackups Mon 07/02/2018 0900 PM 0 Files(s) copied Xcopy SUCCEEDED K: to J:MyUSBBackupsOutlookBak Mon 07/02/2018 0900 PM Ответ №3
Лично я бы изменил этот формат ведения журнала, чтобы он был более дружественным к объекту и использовал его как обычно.
Однако, исходя из того, что вы опубликовали. Вот один из способов сделать это, я уверен, что есть более элегантные способы, но это q & d (быстрый и грязный). Также, как военный ветеринар (20+ лет) и все еще живет и работает в военное время, 0900 9:00, где 2100 – 21:00. 8 ^}… Просто говорю…
# Get the lines in the file ($DataSet = Get-Content -Path ‘.LogFile.txt’) # Results ++++++++++++++++++++Mon 07/02/2018 0900 PM 0 Files(s) copied Xcopy SUCCEEDED K: to J:MyUSBBackups Mon 07/02/2018 0900 PM 0 Files(s) copied Xcopy SUCCEEDED K: to J:MyUSBBackupsOutlookBak Mon 07/02/2018 0900 PM ++++++++++++++++++++Mon 07/03/2018 0900 PM 0 Files(s) copied Xcopy SUCCEEDED K: to J:MyUSBBackups Mon 07/02/2018 0900 PM 0 Files(s) copied Xcopy SUCCEEDED K: to J:MyUSBBackupsOutlookBak Mon 07/02/2018 0900 PM # Get the index of the LastDateEntry, using a string match (RegEx) ($LastDateEntry = (Get-Content -Path ‘.LogFile.txt’ | %{$_ | Select-String -Pattern ‘[+].*’}) | Select -Last 1) # Results ++++++++++++++++++++Mon 07/03/2018 0900 PM # Get the LastDateEntryIndex ($DateIndex = (Get-Content -Path ‘.LogFile.txt’).IndexOf($LastDateEntry)) # Results 5 # Get the data using the index ForEach($Line in $DataSet) { If ($Line.ReadCount -ge $DateIndex) { Get-Content -Path ‘.LogFile.txt’ | Select-Object -Index ($Line.ReadCount) } } # Results ++++++++++++++++++++Mon 07/03/2018 0900 PM 0 Files(s) copied Xcopy SUCCEEDED K: to J:MyUSBBackups Mon 07/02/2018 0900 PM 0 Files(s) copied Xcopy SUCCEEDED K: to J:MyUSBBackupsOutlookBak Mon 07/02/2018 0900 PM