如何使用 PowerShell 替换文件中的多个字符串

我正在编写一个自定义配置文件的脚本。我想在这个文件中替换字符串的多个实例,我尝试使用 PowerShell 来完成这项工作。

对于单个替换,它工作得很好,但是进行多个替换非常慢,因为每次都必须再次解析整个文件,而且这个文件非常大。剧本是这样的:

$original_file = 'path\filename.abc'
$destination_file =  'path\filename.abc.new'
(Get-Content $original_file) | Foreach-Object {
$_ -replace 'something1', 'something1new'
} | Set-Content $destination_file

我想要这样的东西,但是我不知道怎么写:

$original_file = 'path\filename.abc'
$destination_file =  'path\filename.abc.new'
(Get-Content $original_file) | Foreach-Object {
$_ -replace 'something1', 'something1aa'
$_ -replace 'something2', 'something2bb'
$_ -replace 'something3', 'something3cc'
$_ -replace 'something4', 'something4dd'
$_ -replace 'something5', 'something5dsf'
$_ -replace 'something6', 'something6dfsfds'
} | Set-Content $destination_file
221757 次浏览

一种选择是将 -replace操作链接在一起。每行末尾的 `转义换行符,导致 PowerShell 在下一行继续解析表达式:

$original_file = 'path\filename.abc'
$destination_file =  'path\filename.abc.new'
(Get-Content $original_file) | Foreach-Object {
$_ -replace 'something1', 'something1aa' `
-replace 'something2', 'something2bb' `
-replace 'something3', 'something3cc' `
-replace 'something4', 'something4dd' `
-replace 'something5', 'something5dsf' `
-replace 'something6', 'something6dfsfds'
} | Set-Content $destination_file

另一种选择是指定一个中间变量:

$x = $_ -replace 'something1', 'something1aa'
$x = $x -replace 'something2', 'something2bb'
...
$x

假设每行只能有一个 'something1''something2'等,那么可以使用一个查找表:

$lookupTable = @{
'something1' = 'something1aa'
'something2' = 'something2bb'
'something3' = 'something3cc'
'something4' = 'something4dd'
'something5' = 'something5dsf'
'something6' = 'something6dfsfds'
}


$original_file = 'path\filename.abc'
$destination_file =  'path\filename.abc.new'


Get-Content -Path $original_file | ForEach-Object {
$line = $_


$lookupTable.GetEnumerator() | ForEach-Object {
if ($line -match $_.Key)
{
$line -replace $_.Key, $_.Value
break
}
}
} | Set-Content -Path $destination_file

如果可以有多个这样的选项,只需删除 if语句中的 break

为了让 George Howarth 的文章在多个替换中正常工作,你需要删除中断,将输出分配给一个变量($line) ,然后输出变量:

$lookupTable = @{
'something1' = 'something1aa'
'something2' = 'something2bb'
'something3' = 'something3cc'
'something4' = 'something4dd'
'something5' = 'something5dsf'
'something6' = 'something6dfsfds'
}


$original_file = 'path\filename.abc'
$destination_file =  'path\filename.abc.new'


Get-Content -Path $original_file | ForEach-Object {
$line = $_


$lookupTable.GetEnumerator() | ForEach-Object {
if ($line -match $_.Key)
{
$line = $line -replace $_.Key, $_.Value
}
}
$line
} | Set-Content -Path $destination_file

对于流水线一行程序,第三种选择是嵌套--

PS> ("ABC" -replace "B","C") -replace "C","D"
ADD

还有:

PS> ("ABC" -replace "C","D") -replace "B","C"
ACD

这样可以保持执行顺序,易于阅读,并且可以巧妙地放入管道中。我更喜欢使用括号来进行显式控制、自我文档化等等。没有他们也行,但你能相信多少呢?

- Replace 是一个比较运算符,它接受一个对象并返回一个假定修改过的对象。这就是为什么您可以像上面所示的那样堆栈或嵌套它们。

请参阅:

help about_operators

使用 PowerShell 的版本3,您可以将替换调用链接在一起:

 (Get-Content $sourceFile) | ForEach-Object {
$_.replace('something1', 'something1').replace('somethingElse1', 'somethingElse2')
} | Set-Content $destinationFile

只是一个通用的可重复使用的解决方案:

function Replace-String {
[CmdletBinding()][OutputType([string])] param(
[Parameter(Mandatory = $True, ValueFromPipeLine = $True)]$InputObject,
[Parameter(Mandatory = $True, Position = 0)][Array]$Pair,
[Alias('CaseSensitive')][switch]$MatchCase
)
for ($i = 0; $i -lt $Pair.get_Count()) {
if ($Pair[$i] -is [Array]) {
$InputObject = $InputObject |Replace-String -MatchCase:$MatchCase $Pair[$i++]
}
else {
$Regex = $Pair[$i++]
$Substitute = if ($i -lt $Pair.get_Count() -and $Pair[$i] -isnot [Array]) { $Pair[$i++] }
if ($MatchCase) { $InputObject = $InputObject -cReplace $Regex, $Substitute }
else            { $InputObject = $InputObject -iReplace $Regex, $Substitute }
}
}
$InputObject
}; Set-Alias Replace Replace-String

用法:

$lookupTable |Replace 'something1', 'something1aa', 'something2', 'something2bb', 'something3', 'something3cc'

或:

$lookupTable |Replace ('something1', 'something1aa'), ('something2', 'something2bb'), ('something3', 'something3cc')

例如:

'hello world' |Replace ('h','H'), ' ', ('w','W')
HelloWorld