Problem
You want to search for text in a file and replace that text with something new.
Solution
To search and replace text in a file, first store the content of the file in a variable, and then store the replaced text back in that file as shown in Example 74.
Example 74. Replacing text in a file
PS >$filename = "file.txt" PS >$match = "source text" PS >$replacement = "replacement text" PS > PS >$content = GetContent $filename PS >$content This is some source text that we want to replace. One of the things you may need to be careful careful about with Source Text is when it spans multiple lines, and may have different Source Text capitalization. PS > PS >$content = $content creplace $match,$replacement PS >$content This is some replacement text that we want to replace. One of the things you may need to be careful careful about with Source Text is when it spans multiple lines, and may have different Source Text capitalization. PS >$content | SetContent $filename
Discussion
Using PowerShell to search and replace text in a file (or many files!) is one of the best examples of using a tool to automate a repetitive task. What could literally take months by hand can be shortened to a few minutes (or hours, at most).
Notice that the solution uses the –creplace operator to replace text in a casesensitive manner. This is almost always what you will want to do, as the replacement text uses the exact capitalization that you pro
vide. If the text you want to replace is capitalized in several different ways (as in the term “Source Text” from the solution), then search and replace several times with the different possible capitalizations.
Example 74 illustrates what is perhaps the simplest (but actually most common) scenario:
- You work with an ASCII text file.
- You replace some literal text with a literal text replacement.
- You don’t worry that the text match might span multiple lines.
- Your text file is relatively small.
If some of those assumptions don’t hold true, then this discussion shows you how to tailor the way you search and replace within this file.
Work with files encoded in Unicode or another (OEM) code page
By default, the SetContent cmdlet assumes that you want the output file to contain plain ASCII text. If you work with a file in another encoding (for example, Unicode or an OEM code page such as Cyrillic), use the –Encoding parameter of the OutFile cmdlet to specify that:
$content | OutFile –Encoding Unicode $filename $content | OutFile –Encoding OEM $filename
Replace text using a pattern instead of plain text
Although it is most common to replace one literal string with another literal string, you might want to replace text according to a pattern in some advanced scenarios. One example might be swapping first name and last name. PowerShell supports this type of replacement through its support of regular expressions in its replacement operator:
PS >$content = GetContent names.txt PS >$content John Doe Mary Smith PS >$content replace '(.*) (.*)','$2, $1' Doe, John Smith, Mary
Replace text that spans multiple lines
The GetContent cmdlet used in the solution retrieves a list of lines from the file. When you use the –replace operator against this array, it replaces your text in each of those lines individually. If your match spans multiple lines, as shown between lines 3 and 4 in Example 74, the –replace operator will be unaware of the match and will not perform the replacement.
If you want to replace text that spans multiple lines, then it becomes necessary to stop treating the input text as a collection of lines. Once you stop treating the input as a collection of lines, it is also important to use a replacement expression that can ignore line breaks, as shown in Example 75.
Example 75. Replacing text across multiple lines in a file
$filename = GetItem file.txt $singleLine = [System.IO.File]::ReadAllText($filename.FullName) $content = $singleLine creplace "(?s)Source(\s*)Text",'Replacement$1Text'
The first and second lines of Example 75 read the entire content of the file as a sin gle string. It does this by calling the [System.IO.File]::ReadAllText() method from the .NET Framework, since the GetContent cmdlet splits the content of the file into individual lines.
The third line of this solution replaces the text by using a regular expression pattern. The section, Source(\s*)Text, scans for the word Source followed optionally by some whitespace, followed by the word Text. Since the whitespace portion of the regular expression has parentheses around it, we want to remember exactly what that whitespace was. By default, regular expressions do not let newline characters count as whitespace, so the first portion of the regular expression uses the singleline option (?s) to allow newline characters to count as whitespace. The replacement portion of the –replace operator replaces that match with Replacement, followed by the exact whitespace from the match that we captured ($1), followed by Text.
Replace text in large files
The approaches used so far store the entire contents of the file in memory as they replace the text in them. Once we’ve made the replacements in memory, we write the updated content back to disk. This works well when replacing text in small, medium, and even moderately large files. For extremely large files (for example, more than several hundred megabytes), using this much memory may burden your system and slow down your script. To solve that problem, you can work on the files linebyline, rather than with the entire file at once.
Since you’re working with the file linebyline, it will still be in use when you try to write replacement text back into it. You can avoid this problem if you write the replacement text into a temporary file until you’ve finished working with the main file. Once you’ve finished scanning through our file, you can delete it and replace it with the temporary file.
$filename = "file.txt" $temporaryFile = [System.IO.Path]::GetTempFileName()
$match = "source text" $replacement = "replacement text"
GetContent $filename | ForeachObject { $_ creplace $match,$replacement | AddContent $temporaryFile }
RemoveItem $filename MoveItem $temporaryFile $filename