Question:
I’m working on a PowerShell script that finds all the files with PATTERN within a given DIRECTORY, prints out the relevant lines of the document with the PATTERN highlighted, and then replaces the PATTERN with a provided REPLACE word, then saves the file back. So it actually edits the file.
Except I can’t get it to alter the file, because Windows complains about the file already being open. I tried several methods to solve this, but keep running into the issue. Perhaps someone can help:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
param( [string] $pattern = "" ,[string] $replace = "" ,[string] $directory ="." ,[switch] $recurse = $false ,[switch] $caseSensitive = $false) if($pattern -eq $null -or $pattern -eq "") { Write-Error "Please provide a search pattern." ; return } if($directory -eq $null -or $directory -eq "") { Write-Error "Please provide a directory." ; return } if($replace -eq $null -or $replace -eq "") { Write-Error "Please provide a string to replace." ; return } $regexPattern = $pattern if($caseSensitive -eq $false) { $regexPattern = "(?i)$regexPattern" } $regex = New-Object System.Text.RegularExpressions.Regex $regexPattern function Write-HostAndHighlightPattern([string] $inputText) { $index = 0 $length = $inputText.Length while($index -lt $length) { $match = $regex.Match($inputText, $index) if($match.Success -and $match.Length -gt 0) { Write-Host $inputText.SubString($index, $match.Index) -nonewline Write-Host $match.Value.ToString() -ForegroundColor Red -nonewline $index = $match.Index + $match.Length } else { Write-Host $inputText.SubString($index) -nonewline $index = $inputText.Length } } } Get-ChildItem $directory -recurse:$recurse | Select-String -caseSensitive:$caseSensitive -pattern:$pattern | foreach { $file = ($directory + $_.FileName) Write-Host "$($_.FileName)($($_.LineNumber)): " -nonewline Write-HostAndHighlightPattern $_.Line %{ Set-Content $file ((Get-Content $file) -replace ([Regex]::Escape("[$pattern]")),"[$replace]")} Write-Host "`n" Write-Host "Processed: $($file)" } |
The issue is located within the final block of code, right at the Get-ChildItem call. Of course, some of the code in that block is now a bit mangled due to me trying to fix the problem then stopping, but keep in mind the intent of that part of the script. I want to get the content, replace the words, then save the altered text back to the file I got it from.
Any help at all would be greatly appreciated.
Answer:
Removed my previous answer, replacing it with this:
1 2 3 4 5 6 7 8 9 |
Get-ChildItem $directory -recurse:$recurse foreach { $file = ($directory + $_.FileName) (Get-Content $file) | Foreach-object { $_ -replace ([Regex]::Escape("[$pattern]")),"[$replace]") } | Set-Content $file } |
Note:
- The parentheses around
Get-Content
to ensure the file is slurped in one go (and therefore closed). - The piping to subsequent commands rather than inlining.
- Some of your commands have been removed to ensure it’s a simple test.