1
+ # Replace-FileString.ps1
2
+ # Written by Bill Stewart ([email protected] )
3
+ #
4
+ # Replaces strings in files using a regular expression. Supports
5
+ # multi-line searching and replacing.
6
+
7
+ # requires -version 2
8
+
9
+ <#
10
+ . SYNOPSIS
11
+ Replaces strings in files using a regular expression.
12
+
13
+ . DESCRIPTION
14
+ Replaces strings in files using a regular expression. Supports
15
+ multi-line searching and replacing.
16
+
17
+ . PARAMETER Pattern
18
+ Specifies the regular expression pattern.
19
+
20
+ . PARAMETER Replacement
21
+ Specifies the regular expression replacement pattern.
22
+
23
+ . PARAMETER Path
24
+ Specifies the path to one or more files. Wildcards are permitted. Each
25
+ file is read entirely into memory to support multi-line searching and
26
+ replacing, so performance may be slow for large files.
27
+
28
+ . PARAMETER LiteralPath
29
+ Specifies the path to one or more files. The value of the this
30
+ parameter is used exactly as it is typed. No characters are interpreted
31
+ as wildcards. Each file is read entirely into memory to support
32
+ multi-line searching and replacing, so performance may be slow for
33
+ large files.
34
+
35
+ . PARAMETER CaseSensitive
36
+ Specifies case-sensitive matching. The default is to ignore case.
37
+
38
+ . PARAMETER Multiline
39
+ Changes the meaning of ^ and $ so they match at the beginning and end,
40
+ respectively, of any line, and not just the beginning and end of the
41
+ entire file. The default is that ^ and $, respectively, match the
42
+ beginning and end of the entire file.
43
+
44
+ . PARAMETER UnixText
45
+ Causes $ to match only linefeed (\n) characters. By default, $ matches
46
+ carriage return+linefeed (\r\n). (Windows-based text files usually use
47
+ \r\n as line terminators, while Unix-based text files usually use only
48
+ \n.)
49
+
50
+ . PARAMETER Overwrite
51
+ Overwrites a file by creating a temporary file containing all
52
+ replacements and then replacing the original file with the temporary
53
+ file. The default is to output but not overwrite.
54
+
55
+ . PARAMETER Force
56
+ Allows overwriting of read-only files. Note that this parameter cannot
57
+ override security restrictions.
58
+
59
+ . PARAMETER Encoding
60
+ Specifies the encoding for the file when -Overwrite is used. Possible
61
+ values are: ASCII, BigEndianUnicode, Unicode, UTF32, UTF7, or UTF8. The
62
+ default value is ASCII.
63
+
64
+ . INPUTS
65
+ System.IO.FileInfo.
66
+
67
+ . OUTPUTS
68
+ System.String without the -Overwrite parameter, or nothing with the
69
+ -Overwrite parameter.
70
+
71
+ . LINK
72
+ about_Regular_Expressions
73
+
74
+ . EXAMPLE
75
+ C:\>Replace-FileString.ps1 '(Ferb) and (Phineas)' '$2 and $1' Story.txt
76
+ This command replaces the string 'Ferb and Phineas' with the string
77
+ 'Phineas and Ferb' in the file Story.txt and outputs the file. Note
78
+ that the pattern and replacement strings are enclosed in single quotes
79
+ to prevent variable expansion.
80
+
81
+ . EXAMPLE
82
+ C:\>Replace-FileString.ps1 'Perry' 'Agent P' Ferb.txt -Overwrite
83
+ This command replaces the string 'Perry' with the string 'Agent P' in
84
+ the file Ferb.txt and overwrites the file.
85
+ #>
86
+
87
+ [CmdletBinding (DefaultParameterSetName = " Path" ,
88
+ SupportsShouldProcess = $TRUE )]
89
+ param (
90
+ [parameter (Mandatory = $TRUE , Position = 0 )]
91
+ [String ] $Pattern ,
92
+ [parameter (Mandatory = $TRUE , Position = 1 )]
93
+ [String ] [AllowEmptyString ()] $Replacement ,
94
+ [parameter (Mandatory = $TRUE , ParameterSetName = " Path" ,
95
+ Position = 2 , ValueFromPipeline = $TRUE )]
96
+ [String []] $Path ,
97
+ [parameter (Mandatory = $TRUE , ParameterSetName = " LiteralPath" ,
98
+ Position = 2 )]
99
+ [String []] $LiteralPath ,
100
+ [Switch ] $CaseSensitive ,
101
+ [Switch ] $Multiline ,
102
+ [Switch ] $UnixText ,
103
+ [Switch ] $Overwrite ,
104
+ [Switch ] $Force ,
105
+ [String ] $Encoding = " ASCII"
106
+ )
107
+
108
+ begin {
109
+ # Throw an error if $Encoding is not valid.
110
+ $encodings = @ (" ASCII" , " BigEndianUnicode" , " Unicode" , " UTF32" , " UTF7" ,
111
+ " UTF8" )
112
+ if ($encodings -notcontains $Encoding ) {
113
+ throw " Encoding must be one of the following: $encodings "
114
+ }
115
+
116
+ # Extended test-path: Check the parameter set name to see if we
117
+ # should use -literalpath or not.
118
+ function test-pathEx ($path ) {
119
+ switch ($PSCmdlet.ParameterSetName ) {
120
+ " Path" {
121
+ test-path $path
122
+ }
123
+ " LiteralPath" {
124
+ test-path - literalpath $path
125
+ }
126
+ }
127
+ }
128
+
129
+ # Extended get-childitem: Check the parameter set name to see if we
130
+ # should use -literalpath or not.
131
+ function get-childitemEx ($path ) {
132
+ switch ($PSCmdlet.ParameterSetName ) {
133
+ " Path" {
134
+ get-childitem $path - force
135
+ }
136
+ " LiteralPath" {
137
+ get-childitem - literalpath $path - force
138
+ }
139
+ }
140
+ }
141
+
142
+ # Outputs the full name of a temporary file in the specified path.
143
+ function get-tempname ($path ) {
144
+ do {
145
+ $tempname = join-path $path ([IO.Path ]::GetRandomFilename())
146
+ }
147
+ while (test-path $tempname )
148
+ $tempname
149
+ }
150
+
151
+ # Use '\r$' instead of '$' unless -UnixText specified because
152
+ # '$' alone matches '\n', not '\r\n'. Ignore '\$' (literal '$').
153
+ if (-not $UnixText ) {
154
+ $Pattern = $Pattern -replace ' (?<!\\)\$' , ' \r$'
155
+ }
156
+
157
+ # Build an array of Regex options and create the Regex object.
158
+ $opts = @ ()
159
+ if (-not $CaseSensitive ) { $opts += " IgnoreCase" }
160
+ if ($MultiLine ) { $opts += " Multiline" }
161
+ if ($opts.Length -eq 0 ) { $opts += " None" }
162
+ $regex = new-object Text.RegularExpressions.Regex $Pattern , $opts
163
+ }
164
+
165
+ process {
166
+ # The list of items to iterate depends on the parameter set name.
167
+ switch ($PSCmdlet.ParameterSetName ) {
168
+ " Path" { $list = $Path }
169
+ " LiteralPath" { $list = $LiteralPath }
170
+ }
171
+
172
+ # Iterate the items in the list of paths. If an item does not exist,
173
+ # continue to the next item in the list.
174
+ foreach ($item in $list ) {
175
+ if (-not (test-pathEx $item )) {
176
+ write-error " Unable to find '$item '."
177
+ continue
178
+ }
179
+
180
+ # Iterate each item in the path. If an item is not a file,
181
+ # skip all remaining items.
182
+ foreach ($file in get-childitemEx $item ) {
183
+ if ($file -isnot [IO.FileInfo ]) {
184
+ write-error " '$file ' is not in the file system."
185
+ break
186
+ }
187
+
188
+ # Get a temporary file name in the file's directory and create
189
+ # it as a empty file. If set-content fails, continue to the next
190
+ # file. Better to fail before than after reading the file for
191
+ # performance reasons.
192
+ if ($Overwrite ) {
193
+ $tempname = get-tempname $file.DirectoryName
194
+ set-content $tempname $NULL - confirm:$FALSE
195
+ if (-not $? ) { continue }
196
+ write-verbose " Created file '$tempname '."
197
+ }
198
+
199
+ # Read all the text from the file into a single string. We have
200
+ # to do it this way to be able to search across line breaks.
201
+ try {
202
+ write-verbose " Reading '$file '."
203
+ $text = [IO.File ]::ReadAllText($file.FullName )
204
+ write-verbose " Finished reading '$file '."
205
+ }
206
+ catch [Management.Automation.MethodInvocationException ] {
207
+ write-error $ERROR [0 ]
208
+ continue
209
+ }
210
+
211
+ # If -Overwrite not specified, output the result of the Replace
212
+ # method and continue to the next file.
213
+ if (-not $Overwrite ) {
214
+ $regex.Replace ($text , $Replacement )
215
+ continue
216
+ }
217
+
218
+ # Do nothing further if we're in 'what if' mode.
219
+ if ($WHATIFPREFERENCE ) { continue }
220
+
221
+ try {
222
+ write-verbose " Writing '$tempname '."
223
+ [IO.File ]::WriteAllText(" $tempname " , $regex.Replace ($text ,
224
+ $Replacement ), [Text.Encoding ]::$Encoding )
225
+ write-verbose " Finished writing '$tempname '."
226
+ write-verbose " Copying '$tempname ' to '$file '."
227
+ copy-item $tempname $file - force:$Force - erroraction Continue
228
+ if ($? ) {
229
+ write-verbose " Finished copying '$tempname ' to '$file '."
230
+ }
231
+ remove-item $tempname
232
+ if ($? ) {
233
+ write-verbose " Removed file '$tempname '."
234
+ }
235
+ }
236
+ catch [Management.Automation.MethodInvocationException ] {
237
+ write-error $ERROR [0 ]
238
+ }
239
+ } # foreach $file
240
+ } # foreach $item
241
+ } # process
242
+
243
+ end { }
0 commit comments