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


122

我在Windows Server 2008上嵌套了几个配置文件,如下所示:

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专家?


1
让我们知道您的工作方式,或者是否有一些奇怪的格式问题需要解决。关于该问题的一件好事是,它可以在不影响生产代码的情况下进行测试
Robben_Ford_Fan_boy 2010年

Answers:


177

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

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

3
我想补充一点,就是对所有提供的解决方案进行测试都是可行的,但这是最易读的解决方案。我能够将其交给同事,他可以轻松地了解发生了什么。感谢您的协助。
布兰登

11
为了使其在子目录的文件中起作用,您需要“ .PSPath”。有趣的是,当我尝试使此操作在get-content周围不带()时,它在write-content失败,因为文件已锁定。
Frank Schwieterman 2010年

25
短版(使用通用别名):ls *.config -rec | %{ $f=$_; (gc $f.PSPath) | %{ $_ -replace "Dev", "Demo" } | sc $f.PSPath }
Artyom

5
@Artyom不要忘记了.之后ls。被我自己st住了。
Pureferret

5
如果您要删除* .config以在所有文件上运行,则UnauthorizedAccessException也可能由于文件夹引起。你可以-文件过滤器添加到Get-ChildItem ......花了一段时间来弄明白
阿米尔·卡茨

32

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


14

这种方法效果很好:

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

因此,我使用了这个表达式来进行简洁的表达-但我必须替换Get-Content $_Get-Content $_.FullName,并使用Set-Content它的等效项来处理不在根目录下的文件。
马特·惠特菲尔德

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

11

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

7

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

我认为这是已接受答案的简短版本;

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

1
万一其他人遇到这个问题,就像我所做的那样-希望直接从批处理文件中执行它- 在执行这样的命令时,使用foreach-object而不是%别名可能会有所帮助。否则,可能会导致错误:Expressions are only allowed as the first element of a pipeline
Dustin Halstead

短片并不总是更好,而是更清楚长片中发生了什么。
尼克N.19年

@NickN。好吧,对。这取决于您的目标。我将其标记为POB;)
M

6

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

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

4

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

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

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


您的答案的“ .PSPath”部分确实对我有所帮助。但是我必须将内部的“ {$”更改为“ $ _”。我会编辑您的答案,但是我没有使用-creplace部分,而是将接受的答案与.PSPath一起使用
aaaa bbbb
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.