根据下一行查找并替换为PowerShell

时间:2018-11-20 17:19:51

标签: powershell replace

我正在尝试根据要替换的行的下一行内容通过PowerShell查找和替换。例如,以下文本文件:

blahblah 
flimflam 
zimzam

如果string text = "<input type=\"hidden\" id=\"taken\" value=\"BoboBobo\">\n<input type=\"hidden\" id=\"took\" value=\"BaboboBe\""; Regex regex = new Regex(@"(?<=\svalue\s?=\s?\\"").*?(?=\\"")"); foreach (Match match in regex.Matches(text)) { Console.WriteLine(match.Value); } 之后的行是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") } ,则什么都不做。

我曾经有一个想法是用“新东西”代替“ blahblan` flimflam”,但我无法使其正常工作。我想我可能会加入一些换行符。

3 个答案:

答案 0 :(得分:1)

  • 虽然使用System.IO.StreamReader可以正常工作,但是通常更容易使用Get-Content -Raw将文件完整地读入内存 作为单,多行字符串。

    • 如果要考虑性能,您仍然可以直接使用.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(多行)使锚点^ / $每行的开始/结束匹配< / em>(而不是整个字符串)。

  • (?=...)是一个先行断言,可以在不包含匹配部分的情况下进行匹配,因此不会被替换。

  • \r?\n是一种与平台无关的方式,用于匹配 newline 序列/字符:Windows上的CRLF(\r\n),仅适用于LF({{1 }})在类似Unix的平台上。

答案 1 :(得分:0)

使用带有positive lookahead的RegEx,您甚至可以不知道前一行的内容而替换前一行:

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

请参见regex101.com上介绍的RegEx(用不同的转义`n => \ n)

答案 2 :(得分:0)

这个问题要求一个可重用的cmdlet,该cmdlet尽可能支持流传输...

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运算符用于此参数,这意味着它支持正则表达式。如果整个字符串需要匹配,请使用start line and end line anchors,例如:-Match '^Three$'

-Replacement <String>
用于替换目标的字符串。

-Offset <Int> = 0
相对于要替换的匹配字符串的字符串偏移量。默认值为0,表示:替换匹配的字符串。

背景

有关此cmdlet编程的一些背景知识:

  • Input Processing MethodsBegin {...}Process {...}End {...}用于使字符串尽可能快地通过cmdlet,并释放它们以供管道中的下一个cmdlet使用。 。该cmdlet设计用于管道的中间部分(例如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) {...将保留输入字符串的第一个数目(等于偏移量),因为只有稍后才能确定是否需要替换输入字符串
  • 最后(End {...,如果$Offset为负,则释放缓冲区(包含其余的输入字符串)。换句话说,负偏移量(例如-offset -$n)将缓冲$n个字符串,并使输出在输入流后面运行$n个字符串