为什么不能在PowerShell的replace运算符中转换匹配令牌?

问题描述

试图简化一些PowerShell字符串替换代码,但遇到了我无法完全解释的内容。明确地说,我知道如何找到一个子字符串并将其替换/将其转换为另一个值。 我要问的是对此行为的解释,以及为什么这种行为不起作用。

使用常规字符串,无论是变量还是硬编码,我都可以在渲染字符串后修改其​​显示方式。例如:

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

这将通过返回字符串的小写形式来产生预期的输出。但是,我使用-replace运算符进行了尝试,并得到了有趣的结果:

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

我期望将字符串中的匹配模式替换为匹配的大写副本,但这没有发生。我尝试了其他一些越来越丑陋的方法,以查看是否有某种方法可以使它正常工作,但无济于事:

$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

我还尝试了-creplace来代替-replace,但其中的一些方法没有任何效果。为什么在使用PowerShell的-replace运算符时无法转换匹配结果?

我使用PowerShell 5.1和PowerShell 7进行了测试,因此此行为在MS PowerShell和PowerShell Core之间扩展。

解决方法

ToUpper()之前执行,结果大写字符串作为替代参数传递给-replace-$0的大写版本为只是$0,因此与完全省略.ToUpper()没什么不同。

如果要ToUpper()作为替代例程的一部分,则必须pass a scriptblock作为替代参数:

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

这仅在PowerShell 6.2及更高版本中有效,在Windows PowerShell中,您必须直接调用[regex]::Replace()

[regex]::Replace($greeting,'\w+',{param($m) $m.Value.ToUpper()})