用于查找和替换具有特定扩展名的所有文件的 PowerShell 脚本

问题描述 投票:0回答:9

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

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 scripting replace find
9个回答
217
投票

这是我的第一次尝试。

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

36
投票

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


19
投票

这个方法效果很好:

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

16
投票

此 powershell 示例在文件夹及其子文件夹中查找字符串“oo\”的所有实例,将“oo\”替换为“ar\”,并且不会重写不包含字符串“oo\”的文件不破坏文件上次更新日期时间戳的方法,其中未找到字符串:

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 $_ }
           }

12
投票

我发现@Artyom的评论有用,但不幸的是他们没有将其发布为答案。

这是已接受答案的简短版本,在我看来是更好的版本;

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

11
投票

我会选择 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)  
}

10
投票

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

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' 
}

5
投票

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

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

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


0
投票

之前的一些答案会将行结尾转换为 Windows 风格的 CRLF,无论它们最初是否是 UNIX 风格的 LF,并且还可能使用意外的编码损坏文件。

-Replace
运算符使用正则表达式。
.Replace
方法使用文字文本。

(我同时使用 Windows 和 UNIX 行结尾,但已标准化为没有 BOM 的 UTF8。)

指定编码、捕获读取错误以及使用

-Raw
标志一次抓取所有行(及其分隔符)将缓解这些问题。

$utf8 = New-Object Text.UTF8Encoding
$files = Get-ChildItem -Path 'test1.sql', 'test2.sql'
$find = 'an old needle from our haystack'
$replace = 'a new needle for our haystack'

ForEach ($f In $files) {
   $text_content = $Null
   $text_content = Get-Content -PSPath $f.PSPath -Raw -Encoding utf8 -ErrorAction SilentlyContinue
   If ($text_content) {
      $text_content = $text_content.Replace($find, $replace)
      $byte_content = $utf8.GetBytes($text_content)
      Set-Content -Value $byte_content -Encoding Byte -PSPath $f.PSPath
   }
}
© www.soinside.com 2019 - 2024. All rights reserved.