If you try to do a -replace with a string that has two or more dollar signs next to it, one dollar sign gets removed. This happens even if the characters are escaped.
Scenario 1: Two Dollar Signs in String Literal

Scenario 2: More Than Two Dollar Signs in String Literal

Scenario 3: One Dollar Sign in String Literal

Scenario 4: Two Dollar Signs in String Literal (escaped)

Scenario 1: P@$$w0rd
Scenario 2: P@$$$w0rd
Scenario 3: P@$w0rd
Scenario 3: P@$$w0rd
Scenario 1:

Scenario 2:

Scenario 3:

Scenario 4:

$PSVersionTable
```
The -replace replacement operand is not a _literal_: $ has special meaning in it and is used to refer to aspects of the regex matching, such as $& to refer to the entire match and $1 to refer to what the 1st capture group matched.
Therefore, $ characters you want to treat _literally_ must be _escaped_ by _doubling_ them.
(Note that this escaping mechanism is unrelated to PowerShell's `-based escaping).
And that is what happened accidentally in your case: the $$ sequence was interpreted as an _escaped_ $.
Unescaped $ sequences that happen not to form a valid regex-match reference are also retained as literals, but it's better not to rely on that.
You can read more about this here.
The robust workaround is to use the following:
$testString -replace '...', $password.Replace('$', $$')
However, if your replacement operation doesn't actually need _regex_ matching, only simple _substring_ replacement, you can use [string]::Replace() for the entire operation, but note that it is case-_sensitive_ by default:
$testString.Replace('...', $password)
Thank you for explaining! I couldn't find a reference to that kind of escaping anywhere!
We should probably have include a link to Substitutions in Regular Expressions in about_Comparison_Operators as well as mentioning that substitutions happen with -replace in the body of the document.
Someone forgot to post that the symptom exist in PSCore6. Although I would have use the following .NET object replace method:
PS C:\Program Files\PowerShell\6-preview> $password
P@$$word
PS C:\Program Files\PowerShell\6-preview> $password.Length
8
PS C:\Program Files\PowerShell\6-preview> $password.ToString()
P@$$word
PS C:\Program Files\PowerShell\6-preview> "$($password.ToString())"
P@$$word
PS C:\Program Files\PowerShell\6-preview> $testString.replace('%MGR_PWD',"$($password.ToString())");
P@$$word
I mostly use .NET object methods instead of using parameters. But, that's me!
@MaximoTrinidad:
To be clear: the behavior is by design, and it is here to stay.
However, the behavior is poorly documented, hence @BrucePay's suggestion.
Using String.Replace(string oldvalue, string newvalue) may be the simplest solution in _this_ case, but it is by no means a _general_ alternative to -replace; not only do the behaviors differ, -replace offers many more features:
Regex support, which -replace _invariably_ applies.
String.Replace() _only_ supports literal substring replacement - both for matching and in the replacement string.
$ chars. in the replacement string, as you demonstrate.You can _indirectly_ get literal substring replacements with -replace too:
\-escape all regex metacharacters that you want to be treated as literals.[regex]::Escape() on it.$-escape $ instances that you want to be treated as literals (i.e., _double_ $ chars. meant to be literals)..Replace('$', '$$') on it (i.e., take advantage of simple substring replacement via String.Replace()) or, if you want to stick with -replace, use (... -replace '\$', '$$$$').As an aside: thanks to work by @IISResetMe, -replace as of v6.1.0-preview.2 even supports passing a _script block_ as the replacement operand; e.g.,
'1 + 1 = 2' -replace '\d+', { [int] $_.Value * 2 } yields '2 + 2 = 4'
Case-insensitivity.
String.Replace(string oldValue, string newValue), is case-_sensitive_, but you can opt into case-sensitivity via overloads String.Replace(string oldValue, string newValue, bool ignoreCase, cultureinfo culture) or String.Replace(string oldValue, string newValue, System.StringComparison comparisonType)-creplace gives you case-sensitivity (more conveniently) in PowerShell.Support for array-valued LHS operands.
Support for implicit to-string conversion of the LHS.
P.S.: You can find an analogous juxtaposition of -split and String.Split() in this SO answer.
I've created a doc issue at https://github.com/PowerShell/PowerShell-Docs/issues/2416
@blixthecat: Now that the issue is being tracked there, can I ask you to close _this_ one?
Most helpful comment
The
-replacereplacement operand is not a _literal_:$has special meaning in it and is used to refer to aspects of the regex matching, such as$&to refer to the entire match and$1to refer to what the 1st capture group matched.Therefore,
$characters you want to treat _literally_ must be _escaped_ by _doubling_ them.(Note that this escaping mechanism is unrelated to PowerShell's
`-based escaping).And that is what happened accidentally in your case: the
$$sequence was interpreted as an _escaped_$.Unescaped
$sequences that happen not to form a valid regex-match reference are also retained as literals, but it's better not to rely on that.You can read more about this here.
The robust workaround is to use the following:
However, if your replacement operation doesn't actually need _regex_ matching, only simple _substring_ replacement, you can use
[string]::Replace()for the entire operation, but note that it is case-_sensitive_ by default: