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


107

我正在编写用于自定义配置文件的脚本。我想替换该文件中的多个字符串实例,并且尝试使用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

Answers:


167

一种选择是将-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

$ original_file == $ destination_file可以吗?如我在修改与源文件一样的文件?
cquadrini

由于PowerShell cmdlet以流方式输入/输出的方式,我认为将其写入同一管道中的同一文件不起作用。但是,您可以执行类似的操作$c = Get-Content $original_file; $c | ... | Set-Content $original_file
dahlbyk

您是否对使用Set-Content而不是原始编码的文件编码有疑问?例如,UTF-8或ANSI编码。
Kiquenet

1
是的,PowerShell是……那样无益。您能够检测自己的编码,如github.com/dahlbyk/posh-git/blob/...
dahlbyk

24

为了使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

到目前为止,这是迄今为止我所见过的最好的方法。唯一的问题是,为了使用相同的源/目标文件路径,我必须首先将整个文件内容读取到一个变量中。
angularsen,2015年

这似乎是最好的答案,尽管我看到了一些奇怪的行为,它的匹配不正确。也就是说,如果您有一个哈希表,其十六进制值作为字符串(0x0、0x1、0x100、0x10000)和0x10000将匹配0x1。
Lorek

13

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

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

上等+流利的风味
hdoghmen

10

假设每行只能有一个'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

如果你能拥有这些的不止一个,只是删除breakif声明。


我看到TroyBramley在最后一行之前添加了$ line来写任何没有变化的行。好的。就我而言,我只更改了需要更换的所有生产线。
崖顶

8

对于流水线式单行代码的第三个选择是嵌套-replaces:

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

和:

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

这样可以保留执行顺序,易于阅读,并且可以整齐地放入管道中。我更喜欢使用括号来进行显式控制,自我记录等。如果没有括号,它就可以工作,但是您对此有多信任?

-Replace是一个比较运算符,它接受一个对象并返回一个大概被修改的对象。这就是为什么您可以如上所示堆叠或嵌套它们的原因。

请参见:

help about_operators
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.