Найти и заменить PowerShell на основе следующей строки - PullRequest
0 голосов
/ 20 ноября 2018

Я пытаюсь найти и заменить через PowerShell в зависимости от того, что находится на следующей строке строки, которую я хочу заменить.Например, следующий текстовый файл:

blahblah 
flimflam 
zimzam

Если строка после blahblah равна flimflam, замените blahblah на new stuff

Вот код, который у меня пока есть:

$reader = New-Object System.IO.StreamReader($myFile.FullName);
$FileContents=$reader.ReadToEnd()
$reader.Close()


if(the line after "blahblah" == "flimflam") #pseudo code
{
    $FileContents=$FileContents.Replace("blahblah","new stuff")
}

Если следующая строка - это что-то отличное от flimflam, ничего не делать.

Одна из моих идей состояла в том, чтобы заменить «blahblah n` flimflam» на «new stuff»,но я не могу заставить его работать.Я думаю, что я мог бы на что-то с включением символа новой строки, хотя.

Ответы [ 3 ]

0 голосов
/ 21 ноября 2018
  • В то время как использование System.IO.StreamReader работает, обычно проще использовать Get-Content -Raw для чтения файла в память в полном объеме , в качестве одного, несколькихline string.

    • Если производительность вызывает беспокойство, вы все равно можете напрямую использовать типы .NET, в этом случае [System.IO.File]::ReadAllText($myFile.FullName) является гораздо более простой альтернативой.

    • Чтобы явно указать кодировку входного файла, используйте Get-Encoding -Encoding <encoding> / [System.IO.File]::ReadAllText($myFile.FullName, <encoding>),

  • Тип [string] .Replace()метод ограничен литеральной заменой строки, поэтому расширенное сопоставление, такое как ограничение совпадений с полной строкой , не подходит.

    • Использование PowerShellВместо этого используется оператор -replace на основе регулярных выражений.

    • Чтобы избежать путаницы с расширением строки PowerShell (интерполяция строк) в строках с двойными кавычками ("..."), обычно предпочтительнее использовать -replace с одинарными кавычками ('...') строк, которые PowerShell рассматривает как литералы, поэтому вы можете сосредоточиться на regex создает в строке.

PS> $FileContents -replace '(?m)^blahblah(?=\r?\nflimflam$)', 'new stuff'
new stuff
flimflam
zimzam
  • (?m) использует встроенный параметр m (многострочный) чтобы привязки ^ / $ соответствовали началу / концу каждой строки (вместо строки в целом).

  • (?=...)это упреждающее утверждение, которое сопоставляется без включения соответствующей части в общее совпадение, поэтому оно не заменяется.

  • \r?\n - это не зависящий от платформы способ сопоставления символ новой строки последовательность / символ: CRLF (\r\n) в Windows, только LF (\n) на Unix-подобных платформах.

0 голосов
/ 21 ноября 2018

В этом вопросе запрашивается многоразовый командлет, который поддерживает максимально возможную потоковую передачу ...

Replace-String

Function Replace-String {
    [CmdletBinding()][OutputType([String[]])]Param (
        [String]$Match, [String]$Replacement, [Int]$Offset = 0,
        [Parameter(ValueFromPipeLine = $True)][String[]]$InputObject
    )
    Begin {
        $Count = 0
        $Buffer = New-Object String[] ([Math]::Abs($Offset))
    }
    Process {
        $InputObject | ForEach-Object {
            If ($Offset -gt 0) {
                If ($Buffer[$Count % $Offset] -Match $Match) {$Replacement} Else {$_}
            } ElseIf ($Offset -lt 0) {
                If ($Count -ge -$Offset) {If ($_ -Match $Match) {$Replacement} Else {$Buffer[$Count % $Offset]}}
            } Else {
                If ($_ -Match $Match) {$Replacement} Else {$_}
            }
            If ($Offset) {$Buffer[$Count++ % $Offset] = $_}
        }
    }
    End {
        For ($i = 0; $i -gt $Offset; $i--) {$Buffer[$Count++ % $Offset]}
    }
}

Синтаксис

$InputObject | Replace-String [-Match] <String to find>
                              [-Replacement] <Replacement string to use>
                              [-Offset] <Offset relative to the matched string>

Примеры:

Заменить найденную строку:

'One', 'Two', 'Three', 'Four', 'Five' | Replace-String Three X 0
One
Two
X
Four
Five

Заменить строку перед найденной строкой:

'One', 'Two', 'Three', 'Four', 'Five' | Replace-String Three X -1
One
X
Three
Four
Five

Заменить вторую строку перед найденной строкой:

'One', 'Two', 'Three', 'Four', 'Five' | Replace-String Three X -2
X
Two
Three
Four
Five

Заменить строку после найденной строки:

'One', 'Two', 'Three', 'Four', 'Five' | Replace-String Three X 1
One
Two
Three
X
Five

Заменить вторую строку после найденной строки:

'One', 'Two', 'Three', 'Four', 'Five' | Replace-String Three X 2
One
Two
Three
Four
X

Заменить строки до (двух) строк, которыесодержит T:

'One', 'Two', 'Three', 'Four', 'Five' | Replace-String T X -1
X
X
Three
Four
Five

Заменить строки после (двух) строк, содержащих T:

'One', 'Two', 'Three', 'Four', 'Five' | Replace-String T X 1
One
Two
X
X
Five

Специфично для вопроса:

'blahblah', 'flimflam','zimzam' | Replace-String 'flimflam' 'new stuff' -1
new stuff
flimflam
zimzam

Параметры

-InputObject <String[]> (из конвейера)
Поток строк для сопоставления и замены

-Match <String>
Строка для сопоставления в потоке.
Обратите внимание, что для этого параметра используется оператор -Match, что означает, что он поддерживает регулярные выражения.Если вся строка должна совпадать, используйте метки начала и конца строки , например: -Match '^Three$'.

-Replacement <String>
Используемая строкадля замены цели.

-Offset <Int> = 0
Смещение строки относительно совпадающей строки (строк) для замены.По умолчанию это 0, что означает: заменить соответствующие строки.

Фон

Небольшой фон программирования в этом командлете:

  • Методы обработки ввода (Begin {...}, Process {...}, End {...} используются для максимально быстрой передачи строк через командлет и освобождения их для следующего командлета в конвейере. Этот командлет предназначен длясередина конвейера (например, Get-Content $myFile | Replace-String A B 1 | ...). Чтобы использовать из конвейера:
    • Избегайте скобок (например: ($List) | Replace-String A B
    • Избегайте назначения вывода (например,: $Array = ... | Replace-String A B
    • Избегайте параметров, которые читают весь контент (как Get-Content -Raw)
  • Если обаВ случаях (замена позади - с использованием отрицательного смещения - или впереди - с использованием положительного смещения-) требуется буфер с размером смещения ($Buffer = New-Object String[] ([Math]::Abs($Offset)))
  • Для ускорения процесса сценарий циклически повторяетсячерез буфер ($Buffer[$Count % $Offset]) вместо смещения содержащихся элементов
  • If ($Count -ge -$Offset) {... будет содержать первый нюmber входных строк (равно смещению), поскольку только позже можно определить, нужно ли заменить входную строку
  • В конце (End {...), если $Offset отрицательно,буфер (содержащий остальные входные строки) освобождается.Другими словами, отрицательное смещение (например, -offset -$n) будет буферизовать строки $n и заставлять вывод запускать строки $n позади входного потока
0 голосов
/ 20 ноября 2018

С RegEx с положительным прогнозом вы можете заменить предыдущую строку, даже не зная содержания этой строки:

(Get-Content .\SO_53398250.txt -raw) -replace "(?sm)^[^`r`n]+(?=`r?`nflimflam)","new stuff"|
 Set-Content .\SO_53398250_2.txt

См. Объяснение RegEx в regex101.com (с другим экранированием `n => \ n)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...