【问题标题】:PowerShell command to replace a chunk of text in a filePowerShell命令替换文件中的一段文本
【发布时间】:2011-03-22 00:23:40
【问题描述】:

我正在尝试使用 PowerShell 替换文件中的一大段文本。例如,我有一个 .sql 文件,并且我知道需要在该特定文件中替换的 SQL 脚本的确切块。在阅读了一些 PowerShell 替换示例后,看起来 PowerShell 以数组形式返回文件的内容(每一行代表数组中的一个条目)。

例如:

GO
:on error exit
GO
IF (DB_ID(N'$(DatabaseName)') IS NOT NULL)
BEGIN
    ALTER DATABASE [$(DatabaseName)]
    SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
    DROP DATABASE [$(DatabaseName)];
END

GO
PRINT N'Creating $(DatabaseName)...'
GO
CREATE DATABASE [$(DatabaseName)] COLLATE SQL_Latin1_General_CP1_CI_AS
GO

USE [$(DatabaseName)]
.......
.........
..... MORE SQL SCRIPT

我想在上面的文件中替换文本直到 USE [$(DatabaseName)]。

【问题讨论】:

    标签: powershell powershell-2.0


    【解决方案1】:

    这就是我将如何解决这个问题。首先,当您需要获取文件的全部内容以替换多行文本时,不要使用Get-Content。请改用[IO.file]::ReadAllText()

    然后使用-replace 运算符,例如:

    [IO.File]::ReadAllText("$pwd\foo.sql") -replace `
       '(?s).*?(USE \[\$\(DatabaseName\)\].*)$',"foo`n`$1" > foo.sql
    

    这里我将开头的文本替换为“foo”。另请注意,要让 -replace 使用的正则表达式跨换行符匹配,我在正则表达式前面加上 (?s) - 单行模式。

    Mjolinor 提出了一个很好的观点,在替换文本包含可以解释为正则表达式特殊变量的字符的情况下,例如$1$2 等。虽然您可以使用 [regex]::escape() 转义正则表达式,但仍有 PowerShell 代码将 $<something> 解释为变量或子表达式的开头。在这种情况下,只需使用 -replace 运算符捕获您想要保留的部分,然后在第二步中添加新文本,就可以轻松解决,例如:

    $keep = [IO.File]::ReadAllText("$pwd\foo.sql") -replace `
                '(?s).*?(USE \[\$\(DatabaseName\)\].*)$','$1'
    $newText + $keep > foo.sql
    

    请注意,在这种情况下的替换中,我在 $1 周围使用单引号,以防止 PowerShell 解释任何特殊的 PowerShell 字符。这有点像 C# 中的逐字字符串。

    【讨论】:

    • 取决于那块文本被替换成什么,这可能会变得非常丑陋。(恕我直言)
    • 在这种情况下,不要费心尝试在替换操作中插入新文本 - 只需抓住尾随部分即可。然后分别将文本添加到尾部。
    • 或将新文本放入此处的字符串中,并将其用作替换参数。
    • 在发布我的帖子后看到了您的回复。几乎相同的。所以你得到了绿色的勾号:)
    【解决方案2】:

    我最终做了以下事情。我觉得更容易理解。

    $fileContent = [System.Io.File]::ReadAllText($filePath)
    $toReplace = [System.Io.File]::ReadAllText($anotherPath)
    $afterReplace = $fileContent.Replace($toReplace,$newContent)
    [System.Io.Directory]::WriteAllText($filePath,$afterReplace)
    

    【讨论】:

      【解决方案3】:

      Get-Content 中,您可以将ReadCount 设置为0。这将创建一个包含一项的数组。您可以通过$contents[0]访问它:

      $contents = Get-Content file.sql -ReadCount 0
      $contents[0].Replace($oldstring, $newString)
      

      您也可以通过管道将整个内容发送到 Out-String,但我认为第一个选项会更快一些。

      【讨论】:

      • 实际上将 ReadCount 指定为 0 会将整个内容作为一个数组(每个索引一行)发送到管道中。
      • 很高兴知道 - 我认为 -readcount 0 已翻译为 IO.FIle.ReadAllText(),谢谢 Keith,很高兴知道。
      【解决方案4】:

      假设您的替换文本在 newscript.txt 中:

       $new = Get-Content newscript.txt
      
       Get-Content file.sql | % {
           if ($_ -eq 'USE [$(DatabaseName)]') {
               $test = $true
           }
           if ($test) {
               $new += $_
           }
       }
      
       $new | Out-File newfile.sql
      

      【讨论】:

        【解决方案5】:

        脚本解决后,命令行解决。假设您的替换文本在 newscript.txt 中:

        # Read the file $file into an array of lines
        PS > $file = Get-Content 'C:\temp\sql.txt'
        
        # Retrieve the line beginning the replacement
        PS > $begin  =  $file | Select-String -Pattern 'USE \[\$\(DatabaseName\)\]' 
        PS > $begin.linenumber
        17
        
        # Selecting (in a new array) the last computed lines
        PS > $secondPart = $file | Select-Object -Last ($file.count - $begin.linenumber +1) 
        PS > $secondPart
        USE [$(DatabaseName)]
        .......
        .........
        ..... MORE SQL SCRIPT
        
        # Creating the new file
        PS > $new = Get-Content newscript.txt
        PS > ($new + $secondPart) | Out-File newfile.sql
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-05-24
          • 1970-01-01
          • 2021-06-01
          • 1970-01-01
          • 2012-11-02
          • 2018-10-30
          • 2016-02-04
          • 2020-09-27
          相关资源
          最近更新 更多