PowerShell doesn’t include the ability to do a mass search-and-replace. As I was just looking for a good way to do this, it so happened that at around the same time someone asked the question in the newsgroups, and got a good answer from Keith Hill.
I took what Keith wrote, and sort of consolidated it into a one line function:
function Replace-String($find, $replace, $includes)
{
get-childitem $includes | select-string $find -list |% { (get-content $_.Path)
|% { $_ -replace $find, $replace } | set-content $_.Path }
}
(Note: The function contents should all be on one line, I just wrapped it for formatting on the website.)
One point to note that I have a question on: I chose to “filter” (bad choice of word, I realize) the replacement using the “select-string” cmdlet. In his book, Windows PowerShell in Action, Bruce Payette mentions that select-string has been optimized for searching for strings in files, so I figured that even though it meant searching through files twice for a pattern, it might speed up the function if I used select-string to prevent me from iterating over the contents of each and every file, even if they did not contain a match for the search. On another up-side, I suppose it also helps prevent me from indirectly “touching” each file (and thus inadvertantly modifying the timestamp on every searched file).
Was that the right decision to make? Thoughts?


August 22, 2007 at 23:29
Good point, it’s a messy solution, but sometimes it’s what we have to do.
August 22, 2007 at 23:29
This code/function works for simple regular expressions, but if you need to replace across lines it will not work. Unfortunately I can’t seem to get Select-String to work across lines, so you would need to have a $narrow as well as a $find. The @narrow would be a single line regex that would narrow the number of files you would perform the full $find regex on. Here is updated code. This will read the whole file into a string, so it may use a chunk of memory for large files…
Get-ChildItem $includes | Select-String $narrow -list | % { [string]::join(”`n”, (Get-Content $_.Path)) -replace $find, $replace | set-content $_.Path }
April 17, 2008 at 08:10
Hi there
Just what I needed. I think that you may also ommit the first cmdlet (get-childitem) and incorporate it into select-string using the the -path option. Just my two cents