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

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

C:\Projects\Project_1\project1.config
  

C:\Projects\Project_2\project2.config

在我的配置中,我需要像下面这样做一个字符串替换:

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

将成为:

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

我考虑过使用批量脚本,但是没有好的方法可以做到这一点,我听说使用 PowerShell 脚本可以很容易地执行这一点。我已经找到了 find/place 的例子,但是我希望有一种方法可以遍历 C: Projects 目录中的所有文件夹,并找到以’结尾的任何文件。Config’扩展名。当它找到一个值时,我希望它替换我的字符串值。

有没有什么好的资源可以帮助你了解如何做到这一点,或者有没有 PowerShell 专家可以提供一些见解?

169121 次浏览

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

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

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

我将使用 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)
}

这种方法很有效:

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

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

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

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

我发现 @ Artyom 的评论很有用,但不幸的是,他们还没有把它作为一个答案。

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

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

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

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

这将替换所有“旧的”与“新的”大小写敏感的文件夹中的所有文件的工作目录。