我正在尝试根据要替换的行的下一行内容通过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”,但我无法使其正常工作。我想我可能会加入一些换行符。
答案 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编程的一些背景知识:
Begin {...}
,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
个字符串