正则表达式将替换所有文件中的文本块

时间:2016-10-18 23:01:21

标签: powershell

我有一个文件列表,所有文件的扩展名.pkg都是产品的一部分。在每个文件中都有一段看起来像这样的文本:

//VERSION
1.73
//END VERSION

我正在尝试编写一个PowerShell脚本,它将找到该代码块并将1.73替换为1.74。

//VERSION
1.74
//END VERSION

我发现以下内容,但我似乎无法使用正则表达式部分写入来识别代码块。

$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
$dir = $scriptPath + "\Sandbox\" 

Write-Host "*** Modifying Files In $dir to Update Version ***"
Get-ChildItem -Path $dir | ?{$_.Extension -eq ".pkg"} | 
  ForEach-Object {
      $copyto = $dir + $_
      # Load the file's contents, replace commas with spaces
      (Get-Content $copyto) -replace '(?<=//VERSION\r").*?(?="\r//END VERSION)', '1.73' |
         # and write it to the correct folder and file name
         Out-File $copyto             
   }

我知道基本语法有效,因为我使用这个完全相同的代码来循环并替换更容易识别的其他内容。

更新的工作代码

$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
$dir = Join-Path $scriptPath "Sandbox"


Write-Host "*** Modifying Files In $dir to Update Version ***"
Get-ChildItem -Path $dir | ?{$_.Extension -eq ".pkg"} | 
  ForEach-Object {
      $copyto = Join-Path $dir $_
      $foundLine = $false
      Get-Content $copyto | foreach {

        if ($foundLine) {
          # Flag is set. Output the following instead of the line from the file
          '1.74'

          # And clear the flag
          $foundLine = $false

        } else { 

          # Output current line
          $_

          # If we find the version line, set the flag so that
          # we enter the replacement on the next line.
          if ($_ -eq '//VERSION') {
            $foundLine = $true
          }
        }
      } | Out-File ($copyto + '.new')

      Write-Host "*** Deleting $copyto ***"
      Remove-Item $copyto

      Write-Host "*** Renaming ($copyto + '.new') ***"
      Rename-Item -Path ($copyto + '.new') -NewName $copyto

   }

2 个答案:

答案 0 :(得分:1)

默认情况下,Get-Content将文件作为字符串数组返回。每行都会针对您的正则表达式进行独立检查。由于您尝试匹配的模式跨越多行,因此没有任何一行会匹配整个行。

要将整个文件视为包含嵌入换行符的一个字符串,请传递-Raw参数

(Get-Content $copyto -Raw) -replace ...

这是一种替代解决方案,它逐行操作并且不使用正则表达式。对于不熟悉正则表达式的人来说,输入更长,但更容易理解。

$foundLine = $false
Get-Content $copyto | foreach {

  if ($foundLine) {
    # Flag is set. Output the following instead of the line from the file
    '1.74'

    # And clear the flag
    $foundLine = $false

  } else { 

    # Output current line
    $_

    # If we find the version line, set the flag so that
    # we enter the replacement on the next line.
    if ($_ -eq '//VERSION') {
      $foundLine = $true
    }
  }
} | Out-File ($copyto + '.new')

不相关:在连接路径时,首选Join-Path进行字符串连接。它有助于避免错过反斜杠问题,并使您的代码更加健壮。

$dir = Join-Path $scriptPath "Sandbox"
...
$copyto = Join-Path $dir $_

答案 1 :(得分:1)

就像Ryan向您展示的那样,您必须使用-raw开关来获取Get-Content cmdlet。但是,正则表达式中还有一些错误:

  1. 你必须使用反斜杠来逃避斜杠。
  2. 除非您知道使用了哪个新行字符,否则应使用\r?\n来读取新行。
  3. 你的正则表达式中有两个双引号,不适合那里。
  4. 您正在用1.73替换实际值,但您可能希望将其更改为1.74
  5. 所以替换这一行:

    (Get-Content $copyto -raw) -replace '(?<=\/\/VERSION)(\r?\n).*?(\r?\n)(?=\/\/END VERSION)', '${1}1.74${2}'