我有几个像这样嵌套的配置文件:
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 专家可以提供一些见解吗?
这是我的第一次尝试。
$configFiles = Get-ChildItem . *.config -rec
foreach ($file in $configFiles)
{
(Get-Content $file.PSPath) |
Foreach-Object { $_ -replace "Dev", "Demo" } |
Set-Content $file.PSPath
}
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
。
这个方法效果很好:
gci C:\Projects *.config -recurse | ForEach {
(Get-Content $_ | ForEach {$_ -replace "old", "new"}) | Set-Content $_
}
此 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 $_ }
}
我发现@Artyom的评论有用,但不幸的是他们没有将其发布为答案。
这是已接受答案的简短版本,在我看来是更好的版本;
ls *.config -rec | %{$f=$_; (gc $f.PSPath) | %{$_ -replace "Dev", "Demo"} | sc $f.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)
}
我编写了一个小辅助函数来替换文件中的文本:
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'
}
进行递归替换时,需要包含路径和文件名:
Get-ChildItem -Recurse | ForEach { (Get-Content $_.PSPath |
ForEach {$ -creplace "old", "new"}) | Set-Content $_.PSPath }
这将在当前目录的文件夹的所有文件中将所有“旧”替换为“新”(区分大小写)。
之前的一些答案会将行结尾转换为 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
}
}