PowerShell Script to Find and Replace for all Files with a Specific Extension
PowershellScriptingReplaceFindPowershell Problem Overview
I have several configuration files nested like such:
C:\Projects\Project_1\project1.config
C:\Projects\Project_2\project2.config
In my configuration I need to do a string replace like such:
<add key="Environment" value="Dev"/>
will become:
<add key="Environment" value="Demo"/>
I thought about using batch scripting, but there was no good way to do this, and I heard that with PowerShell scripting you can easily perform this. I have found examples of find/replace, but I was hoping for a way that would traverse all folders within my C:\Projects directory and find any files that end with the '.config' extension. When it finds one, I want it to replace my string values.
Any good resources to find out how to do this or any PowerShell gurus that can offer some insight?
Powershell Solutions
Solution 1 - Powershell
Here a first attempt at the top of my head.
$configFiles = Get-ChildItem . *.config -rec
foreach ($file in $configFiles)
{
(Get-Content $file.PSPath) |
Foreach-Object { $_ -replace "Dev", "Demo" } |
Set-Content $file.PSPath
}
Solution 2 - Powershell
PowerShell is a good choice ;) It is very easy to enumerate files in given directory, read them and process.
The script could look like this:
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"))
}
I split the code to more lines to be readable for you.
Note that you could use Set-Content instead of [IO.File]::WriteAllText
, but it adds new line at the end. With WriteAllText
you can avoid it.
Otherwise the code could look like this: $c | Set-Content $_.FullName
.
Solution 3 - Powershell
This approach works well:
gci C:\Projects *.config -recurse | ForEach {
(Get-Content $_ | ForEach {$_ -replace "old", "new"}) | Set-Content $_
}
- Change "old" and "new" to their corresponding values (or use variables).
- Don't forget the parenthesis -- without which you will receive an access error.
Solution 4 - Powershell
I would go with xml and 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)
}
Solution 5 - Powershell
This powershell example looks for all instances of the string "\foo" in a folder and its subfolders, replaces "\foo" with "\bar" AND DOES NOT REWRITE files that don't contain the string "\foo" This way you don't destroy the file last update datetime stamps where the string was not found:
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 $_ }
}
Solution 6 - Powershell
I found @Artyom's comment useful but unfortunately they have not posted it as an answer.
This is the short version, in my opinion a better one, of the accepted answer;
ls *.config -rec | %{$f=$_; (gc $f.PSPath) | %{$_ -replace "Dev", "Demo"} | sc $f.PSPath}
Solution 7 - Powershell
I have written a little helper function to replace text in a file:
function Replace-TextInFile
{
Param(
[string]$FilePath,
[string]$Pattern,
[string]$Replacement
)
[System.IO.File]::WriteAllText(
$FilePath,
([System.IO.File]::ReadAllText($FilePath) -replace $Pattern, $Replacement)
)
}
Example:
Get-ChildItem . *.config -rec | ForEach-Object {
Replace-TextInFile -FilePath $_ -Pattern 'old' -Replacement 'new'
}
Solution 8 - Powershell
When doing recursive replacement, the path and filename need to be included:
Get-ChildItem -Recurse | ForEach { (Get-Content $_.PSPath |
ForEach {$ -creplace "old", "new"}) | Set-Content $_.PSPath }
This wil replace all "old" with "new" case-sensitive in all the files of your folders of your current directory.