Reputation: 2696
I am doing a string replacement in PowerShell. I have no control over the strings that are being replaced, but I can reproduce the issue I'm having this way:
> 'word' -replace 'word','@#$+'
@#word
When the actual output I need is
> 'word' -replace 'word','@#$+'
@#$+
The string $+
is being expanded to the word being replaced, and there's no way that I can find to stop this from happening. I've tried escaping the $
with \
(as if it was a regex), with backtick `
(as is the normal PowerShell way). For example:
> 'word' -replace 'word',('@#$+' -replace '\$','`$')
@#`word
How can I replace with a literal $+
in PowerShell? For what it's worth I'm running PowerShell Core 6, but this is reproducible on PowerShell 5 as well.
Upvotes: 4
Views: 43079
Reputation: 439277
tl;dr:
Double the $
in your replacement operand to use it verbatim:
PS> 'word' -replace 'word', '@#$$+' # note the doubled '$'
@#$+
PowerShell's -replace
operator:
uses a regex (regular expression) as the search (1st) operand.
[regex]::Escape()
\
-escape individual characters that would otherwise be interpreted as regex metacharacters.uses a non-literal string that can refer to what the regex matched as the replacement (2nd) operand, via $
-prefixed tokens such as $&
or $+
(see link above or Substitutions in Regular Expressions).
$
chars. in it, which is programmatically most easily done with .Replace('$', '$$')
(see below).If both your search string and your replacement string are to be used verbatim, consider using the [string]
type's .Replace()
method instead, as shown in Brandon Olin's helpful answer.
Caveat: .Replace()
is case-sensitive by default, whereas -replace
is case-insensitive (as PowerShell generally is); use a different .Replace()
overload for case-insensitivity, or, conversely, use the -creplace
variant in PowerShell to get case-sensitivity.
.Replace()
example:'FOO'.Replace('o', '@', 'CurrentCultureIgnoreCase')
.Replace()
only accepts a single string as input, whereas -replace
accepts an array of strings as the LHS; e.g.:
'hi', 'ho' -replace 'h', 'f' # -> 'fi', 'fo'
.Replace()
is faster than -replace
, though that will only matter in loops with high iteration counts.
If you were to stick with the -replace
operator in your case:
As stated, doubling the $
in your replacement operand ensures that they're treated verbatim in the replacement:
PS> 'word' -replace 'word', '@#$$+' # note the doubled '$$'
@#$+
To do this simple escaping programmatically, you can leverage the .Replace()
method:
'word' -replace 'word', '@#$+'.Replace('$', '$$')
You could also do it with a nested -replace
operation, but that gets unwieldy (\$
escapes a $
in the regex; $$
represent a single $
in the replacement string):
# Same as above.
'word' -replace 'word', ('@#$+' -replace '\$', '$$$$')
To put it differently: The equivalent of:
'word'.Replace('word', '@#$+')
is (note the use of the case-sensitive variant of the -replace
operator, -creplace
):
'word' -creplace [regex]::Escape('word'), '@#$+'.Replace('$', '$$')
However, as stated, if both the search string and the replacement operand are to be used verbatim, using .Replace()
is preferable, both for concision and performance.
Upvotes: 1
Reputation: 27516
It's confusing how these codes aren't documented under "about comparison operators". Except I have a closed bug report about them underneath if you look, 'wow, where are all these -replace 2nd argument codes documented?'. https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_comparison_operators?view=powershell-7 That's become my reference. "$+" means "Substitutes the last submatch captured". Doubling up the dollar signs works, 'substitutes a single "$" literal':
'word' -replace 'word','@#$$+'
@#$+
Or use the scriptblock version of the second argument in PS 6 and above (may be slower):
'word' -replace 'word',{'@#$+'}
Upvotes: 6
Reputation: 412
Instead of using the -replace
operator, you can use the .Replace()
method like so:
PS> 'word'.Replace('word','@#$+')
@#$+
The .Replace()
method comes from the .NET String class whereas the -Replace
operator is implemented using System.Text.RegularExpressions.Regex.Replace()
.
More info here: https://vexx32.github.io/2019/03/20/PowerShell-Replace-Operator/
Upvotes: 12
Reputation: 181
I couldn't find it documented but the "Visual Basic" style escaping rule worked, repeat the character.
'word' -replace 'word','@#$$+'
gives you: @#$+
Upvotes: 1