【问题标题】:How to replace multiple strings in a file using PowerShell如何使用 PowerShell 替换文件中的多个字符串
【发布时间】:2011-03-25 02:40:15
【问题描述】:

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

【问题讨论】:

    标签: powershell replace


    【解决方案1】:

    一种选择是将-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 可以吗?就像我正在修改与我的源相同的文件?
    • 由于 PowerShell cmdlet 流式传输其输入/输出的方式,我认为在同一管道中写入同一文件是行不通的。但是,您可以执行 $c = Get-Content $original_file; $c | ... | Set-Content $original_file 之类的操作。
    • 您是否对使用不保留原始编码的Set-Content 进行文件编码有问题?例如 UTF-8 或 ANSI 编码。
    • 是的,PowerShell 是......像那样没有帮助。您必须自己检测编码,例如github.com/dahlbyk/posh-git/blob/…
    • 这个解决方案对我来说失败了。处理时间较长,并且生成了一个非常大的文件,而不是现有文件中的 30 行短文件。
    【解决方案2】:

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

    【讨论】:

    • 我看到 TroyBramley 在最后一行之前添加了 $line 以写入任何没有更改的行。好的。就我而言,我只更改了需要替换的每一行。
    【解决方案3】:

    要让 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
    

    【讨论】:

    • 这是迄今为止我见过的最好的方法。唯一的问题是我必须先将整个文件内容读取到一个变量中才能使用相同的源/目标文件路径。
    • 这看起来是最好的答案,尽管我看到了一些奇怪的行为,它匹配不正确。即,如果您有一个哈希表,其中十六进制值作为字符串(0x0、0x1、0x100、0x10000)和 0x10000 将匹配 0x1。
    【解决方案4】:

    第三个选项,对于流水线的单线是嵌套 -replaces:

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

    还有:

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

    这保留了执行顺序,易于阅读,并且很适合管道。我更喜欢使用括号来进行显式控制、自我文档等。没有它们它也可以工作,但你对它的信任度有多高?

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

    请看:

    help about_operators
    

    【讨论】:

      【解决方案5】:

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

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

      【讨论】:

      • 做工精细+风味流畅
      • 只要你不需要RegEx
      • 问题在哪里提到了正则表达式?
      【解决方案6】:

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

      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
      

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-09-29
      • 2015-11-07
      • 2023-03-28
      • 2021-03-20
      • 1970-01-01
      • 2017-06-23
      • 1970-01-01
      相关资源
      最近更新 更多