-2

Was trying to simplify some PowerShell string replacement code, and came across something I can't quite explain. To be clear, I know how to find a substring and replace it/transform it with another value. What I'm asking for here is an explanation of this behavior and why it doesn't work this way.

With a regular string, whether it's a variable or hard-coded, I can modify the string how I see fit after it has been rendered. For example:

($greeting = 'HELLO'.ToLower()) # ===> hello

This produces the expected output by returning a lowercase copy of the string. However, I tried this using the -replace operator and was met with an interesting result:

$greeting -replace '\w+', '$0'.ToUpper() # ===> hello

I was expecting the matched pattern in the string to be replaced with an uppercase copy of the match, but this isn't happening. I tried a few other increasingly ugly methods to see if there was some way to get this to work but to no avail:

$greeting -replace '\w+', ('$0').ToUpper() # ===> hello
$greeting -replace '\w+', "$(('$0').ToUpper())" # ===> hello
$greeting -replace '\w+', "`$0".ToUpper() # === hello
$greeting -replace '\w+', '`$0'.ToString().ToUpper() # ===> hello
$greeting -replace '\w+', (('$0').ToString()).ToUpper() # ===> hello

I also tried -creplace in place of -replace for a few of these but that also didn't have any effect. Why am I unable to transform the result of a match when using PowerShell's -replace operator?

I tested this with both PowerShell 5.1 and 7 so this behavior extends between MS PowerShell and PowerShell Core.

codewario
  • 19,553
  • 20
  • 90
  • 159
  • Or `'hello' | % toupper` – js2010 Sep 03 '20 at 19:58
  • I don't believe this is a duplicate of the questions linked above. I'm asking about the behavior here and why it behaves this way, not asking how to transform text found with a regular expression. – codewario Sep 04 '20 at 12:32
  • Sometimes the timing of the replace 2nd arg isn't ideal. But you need ps 7 to use the scriptblock version. [regex]::replace() is a good idea. – js2010 Sep 04 '20 at 13:34

1 Answers1

2

ToUpper() is executed before the resulting upper-case string is passed to -replace as the substitute argument - and the upper-case version of $0 is just $0, so no different from omitting .ToUpper() entirely.

If you want ToUpper() called as part of the substitution routine you have to pass a scriptblock as the substitute argument:

$greeting -replace '\w+', {$_.Value.ToUpper()}

This only works in PowerShell 6.2 and up, in Windows PowerShell you have to call [regex]::Replace() directly:

[regex]::Replace($greeting, '\w+', {param($m) $m.Value.ToUpper()})
Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206