【问题标题】:PowerShell Script to Find and Replace for all Files with a Specific Extension用于查找和替换具有特定扩展名的所有文件的 PowerShell 脚本
【发布时间】:2011-02-19 17:38:31
【问题描述】:

我有几个这样嵌套的配置文件:

C:\Projects\Project_1\project1.config
  
C:\Projects\Project_2\project2.config

在我的配置中,我需要像这样进行字符串替换:

<add key="Environment" value="Dev"/>

会变成:

<add key="Environment" value="Demo"/>

我曾想过使用批处理脚本,但没有好的方法可以做到这一点,而且我听说使用 PowerShell 脚本可以轻松执行此操作。我找到了查找/替换的示例,但我希望有一种方法可以遍历我的 C:\Projects 目录中的所有文件夹并找到任何以“.config”扩展名结尾的文件。当它找到一个时,我希望它替换我的字符串值。

是否有任何好的资源来了解如何执行此操作或任何 PowerShell 专家可以提供一些见解?

【问题讨论】:

  • 让我们知道您的进展情况,或者需要解决的文件是否存在一些奇怪的格式问题。这个问题的一个好处是它在不影响生产代码的情况下进行测试
  • 好的,我也来这里寻找 PowerShell,但我几乎被吸引到接受“没有[批量]的好方法”挑战。幸运的是,如果您向下看足够远 on this question,会有一些令人印象深刻的 cmd.exe/DOS 风格的答案。

标签: powershell scripting replace find


【解决方案1】:

这是我头顶上的第一次尝试。

$configFiles = Get-ChildItem . *.config -rec
foreach ($file in $configFiles)
{
    (Get-Content $file.PSPath) |
    Foreach-Object { $_ -replace "Dev", "Demo" } |
    Set-Content $file.PSPath
}

【讨论】:

  • 我想补充一点,测试提供的解决方案都有效,但这个是最容易阅读的。我能够把这个交给一位同事,他很容易理解发生了什么。感谢您的帮助。
  • 要在子目录中的文件中工作,您需要“.PSPath”。有趣的是,当我尝试在 get-content 周围没有 () 的情况下完成这项工作时,它在 write-content 失败,因为文件被锁定。
  • 短版(常用别名):ls *.config -rec | %{ $f=$_; (gc $f.PSPath) | %{ $_ -replace "Dev", "Demo" } | sc $f.PSPath }
  • @Artyom 不要忘记ls 后面的.。我自己也被那个刺痛了。
  • 如果您将删除 *.config 以在所有文件上运行,也可能由于文件夹而导致 UnauthorizedAccessException。您可以将 -File 过滤器添加到 Get-ChildItem... 花了一段时间才弄清楚
【解决方案2】:

PowerShell 是一个不错的选择;) 枚举给定目录中的文件、读取它们并进行处理非常容易。

脚本可能如下所示:

Get-ChildItem C:\Projects *.config -recurse |
    Foreach-Object {
        $c = ($_ | Get-Content) 
        $c = $c -replace '<add key="Environment" value="Dev"/>','<add key="Environment" value="Demo"/>'
        [IO.File]::WriteAllText($_.FullName, ($c -join "`r`n"))
    }

我将代码拆分为更多行以便您阅读。 请注意,您可以使用 Set-Content 代替 [IO.File]::WriteAllText,但它会在末尾添加新行。使用WriteAllText 可以避免它。

否则代码可能如下所示:$c | Set-Content $_.FullName

【讨论】:

    【解决方案3】:

    这种方法效果很好:

    gci C:\Projects *.config -recurse | ForEach {
      (Get-Content $_ | ForEach {$_ -replace "old", "new"}) | Set-Content $_ 
    }
    
    • 将“旧”和“新”更改为相应的值(或使用变量)。
    • 不要忘记括号 - 如果没有括号,您将收到访问错误。

    【讨论】:

    • 所以我用这个来表达简洁 - 但我必须用 Get-Content $_.FullName 替换 Get-Content $_Set-Content 的等价物,以便它处理不在根目录下的文件.
    【解决方案4】:

    我会选择 xml 和 xpath:

    dir C:\Projects\project_*\project*.config -recurse | foreach-object{  
       $wc = [xml](Get-Content $_.fullname)
       $wc.SelectNodes("//add[@key='Environment'][@value='Dev']") | Foreach-Object {$_.value = 'Demo'}  
       $wc.Save($_.fullname)  
    }
    

    【讨论】:

      【解决方案5】:

      此 powershell 示例在文件夹及其子文件夹中查找字符串 "\foo\" 的所有实例,将 "\foo\" 替换为 "\bar\" 并且不重写不包含字符串 " \foo\" 这样您就不会破坏未找到字符串的文件上次更新日期时间戳:

      Get-ChildItem  -Path C:\YOUR_ROOT_PATH\*.* -recurse 
       | ForEach {If (Get-Content $_.FullName | Select-String -Pattern '\\foo\\') 
                 {(Get-Content $_ | ForEach {$_ -replace '\\foo\\', '\bar\'}) | Set-Content $_ }
                 }
      

      【讨论】:

        【解决方案6】:

        我发现@Artyom's comment 很有用,但不幸的是他们没有将其发布为答案。

        这是公认答案的简短版本,我认为这是一个更好的版本;

        ls *.config -rec | %{$f=$_; (gc $f.PSPath) | %{$_ -replace "Dev", "Demo"} | sc $f.PSPath}
        

        【讨论】:

        • 如果其他人遇到这个问题,就像我一样——希望直接从批处理文件中执行——在执行像这样的命令。否则可能会报错:Expressions are only allowed as the first element of a pipeline
        【解决方案7】:

        我写了一个小辅助函数来替换文件中的文本:

        function Replace-TextInFile
        {
            Param(
                [string]$FilePath,
                [string]$Pattern,
                [string]$Replacement
            )
        
            [System.IO.File]::WriteAllText(
                $FilePath,
                ([System.IO.File]::ReadAllText($FilePath) -replace $Pattern, $Replacement)
            )
        }
        

        例子:

        Get-ChildItem . *.config -rec | ForEach-Object { 
            Replace-TextInFile -FilePath $_ -Pattern 'old' -Replacement 'new' 
        }
        

        【讨论】:

          【解决方案8】:

          递归替换时,需要包含路径和文件名:

          Get-ChildItem -Recurse | ForEach {  (Get-Content $_.PSPath | 
          ForEach {$ -creplace "old", "new"}) | Set-Content $_.PSPath }
          

          这将在您当前目录的文件夹的所有文件中将所有“旧”替换为“新”区分大小写。

          【讨论】:

          • 您的回答中的“.PSPath”部分确实帮助了我。但是我不得不将内部的“{$”更改为“$_”。我会编辑你的答案,但我没有使用你的 -creplace 部分——我正在使用接受的答案和 .PSPath
          猜你喜欢
          • 2019-06-26
          • 1970-01-01
          • 2012-01-26
          • 2021-11-09
          • 1970-01-01
          • 1970-01-01
          • 2011-03-13
          • 2011-12-22
          • 2011-03-26
          相关资源
          最近更新 更多