Powershell Replace-String Function

Posted March 28th @ 7:48 am by aaron

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?

2 Trackbacks/Pingbacks

  1. Pingback: Refactoring C# with PowerShell - Aaron Lerch on January 4, 2008
  2. Pingback: Refactoring C# with PowerShell | Aaron Lerch on January 4, 2008

3 Comments

  1. aaron
    August 22, 2007 at 23:29

    Good point, it’s a messy solution, but sometimes it’s what we have to do. :)

  2. schallm
    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 }

  3. Anonymous
    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 ;)

Leave a comment

OpenID Login

Standard Login

Options:

Size

Colors