Martin Brandl’s answer provides an elegant and effective solution, but it’s worth digging deeper:
PowerShell’s -replace
operator (... -replace <search>[, <replace>]
):
-
Takes a regular expression as its first operand,
<search>
(the search expression), and invariably matches globally, i.e., it replaces all matches.'bar' -replace '[ra]', '@'
->'b@@'
- If you want to replace a literal string, you must either individually
\
-escape any regex metacharacters (e.g..
) or use the result from a[regex]::Escape()
call:'bar.none' -replace '\.', '-'
->'bar-none'
'bar.none' -replace [regex]::Escape('.'), '-'
->'bar-none'
-
Specifying a replacement expression,
<replace>
, is optional, in which case the empty string is substituted for what<search>
matched, resulting in its effective removal.'bar' -replace '[ra]'
->'b'
-
If
<replace>
is specified, it supports two forms:-
v6.1+ (PowerShell Core only): A script block (
{ ... }
) as the replacement operand, which offers fully dynamic calculation of the replacement string on a per-match basis:'Level 41' -replace '\d+', { 1 + $_.Value }
->'Level 42'
-
A string containing an expression that can reference what the regular expression captured (and didn’t capture) – such as
$&
to refer to what was matched – explained in detail below.'bar' -replace '[ra]', '{$&}'
->'b{a}{r}'
-
-
-replace
matches case-insensitively (and can also be written as-ireplace
); to perform case-sensitive matching, use the form-creplace
.
The “replacement language” for referencing regex captures in a string-typed <replace>
operand is itself not a regular expression – no matching happens there, only references to the results of the regex matching are supported, via $
-prefixed placeholders that are not to be confused with PowerShell variables.
-
PowerShell’s documentation (now) briefly explains the syntax of replacement strings in its conceptual
about_Comparison_Operators
help topic. -
For the full picture, refer to the Substitutions in Regular Expressions .NET framework help topic, which is applicable because PowerShell’s
-replace
uses theRegex.Replace()
method
behind the scenes.
For convenience, here are the references supported in the <replace>
string (excerpted from the page linked above, with emphasis and annotations added):
-
$number
(e.g.,$1
) … Includes the last substring matched by the capture group that is identified by the1
-basednumber
in the replacement string:-
Including
(...)
, a parenthesized subexpression, in the regex implicitly creates a capture group (capturing group). By default, such capture groups are unnamed and must be referenced by their1
-based (decimal) index reflecting the order in which they appear in the regex, so that$1
refers to what the 1st group in your regex captured,$2
to what the 2nd captured, … -
The form
${number}
(e.g.,${1}
) for disambiguation of the number is also supported (e.g., to make sure that$1
is recognized even if followed by, say,000
, use${1}000
). -
Instead of relying on indices to refer to unnamed capture groups, you can name capture groups and refer to them by name – see next point.
-
If you’re not interested in what the capture group matched, you can opt to ignore it by turning it into a non-capturing group with
(?:...)
.
-
-
${name}
… Includes the last substring matched by the named capture group that is designated by(?<name>...)
in the replacement string. -
$$
… Includes a single"$"
literal in the replacement string. -
$&
… Includes a copy of the entire match in the replacement string ($0
works too, even though it isn’t directly documented). -
$`
… Includes the text of the input string before the match in the replacement string. -
$'
… Includes the text of the input string after the match in the replacement string. -
$+
… Includes the last group captured in the replacement string. [This relieves you of the need to know the last group’s specific index.] -
$_
… Includes the entire input string in the replacement string.
Finally, note that:
-
-replace
invariably matches globally, so if the input string contains multiple matches, the replacements above apply to each match. -
It is generally preferable to use
'...'
(single quotes) for both the regex and the replacement string, because single-quoted strings are non-expanding (non-interpolating), and therefore avoid confusion with PowerShell’s own up-front expansions of$
-prefixed tokens and interpretation of`
chars.
If you do need to include PowerShell variables or expressions, you have three options:-
Use
"..."
(expandable strings) and`
-escape$
instances that are meant for the regex engine; e.g.,`$1
in the following example:
'abc' -replace '(a)', "[`$1]-$HOME-"
which yields something like[a]-C:\Users\jdoe-bc
-
Build your string from literal pieces and variable references using string concatenation (
+
); e.g.:
'abc' -replace '(a)', ('[$1]-' + $HOME + '-')
-
Use
-f
, the string-formatting operator string concatenation; e.g.:
'abc' -replace '(a)', ('[$1]-{0}-' -f $HOME)
-
-
Given that you need to use
$$
to escape a literal$
in the replacement string, use the following idiom to use a variable whose value you want to use literally:... -replace <search>, $var.Replace('$', '$$')
- This relies on the
[string]::Replace()
method performing literal substring replacements.
On a side note, this method is an alternative to-replace
in simple cases, but note that it is case-sensitive by default. - Alternatively, use a nested
-replace
operation, but the syntax is tricky due to the escaping requirements:
... -replace <search>, ($var -replace '\$', '$$$$')