如何替换PowerShell函数并在新函数中调用旧版本?
解决PowerShell替换函数后调用旧版本失效的问题
我尝试替换PowerShell的Invoke-RestMethod函数为带缓存功能的新版本,想在新函数内部调用旧版本,但执行时发现$oldInvoke.Invoke实际调用的是新版本而非旧版本,无法实现预期的缓存效果。
我的场景是为Web请求添加缓存,编写的代码如下:
$global:useCache = $true $oldInvoke = ${function:Invoke-RestMethod} function Remove-CachedRequests { $cachePath = "C:\tmp\Cache" Get-ChildItem -Path $cachePath -Include *.* -File -Recurse | foreach { $_.Delete()} } function Invoke-RestMethod { Param($Uri, $Method, $Headers) if ($global:useCache) { return _InvokeWithCache $Uri $Method $Headers } return _InvokeWithoutCache $Uri $Method $Headers } function GetFilename { Param($Uri, $Method, $Headers) $md5 = New-Object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider $bytes = [Text.Encoding]::ASCII.GetBytes($Uri + $Method + $Header) return [System.BitConverter]::ToString($md5.ComputeHash($bytes)) } function _InvokeWithoutCache { Param($Uri, $Method, $Headers) return $oldInvoke.Invoke(@{"Uri" = $url; "Method" = $Method; "Headers" = $Headers}) } function _InvokeWithCache { Param($Uri, $Method, $Headers) $cachePath = "C:\tmp\Cache" $filename = GetFilename $Uri $Method $Headers $cache = Get-ChildItem -Filter $filename -Path $cachePath if ($cache.Length -gt 0) { return Import-Clixml $cache[0] } $res = _InvokeWithoutCache $Uri $Method $Headers $res | Export-Clixml "${cachePath}\${filename}" return $res }
问题原因
- 调用方式错误:用
${function:Invoke-RestMethod}获取的旧函数是ScriptBlock对象,直接调用Invoke()方法时,执行上下文仍会使用当前作用域的函数定义,导致递归调用新版本的Invoke-RestMethod,而非原始内置命令。 - 参数名错误:
_InvokeWithoutCache里把参数$Uri写成了$url,会导致参数传递失败。 - 变量名错误:
GetFilename里的$Header应为$Headers,会导致哈希计算错误。
解决方案
方法一:用&操作符调用旧ScriptBlock
修改_InvokeWithoutCache函数,用&操作符执行保存的旧函数脚本块,同时修正参数名:
function _InvokeWithoutCache { Param($Uri, $Method, $Headers) # 使用&操作符确保调用原始版本的函数 & $oldInvoke -Uri $Uri -Method $Method -Headers $Headers }
方法二:直接获取原始Cmdlet对象
如果需要更彻底的隔离,直接获取Invoke-RestMethod的原始Cmdlet命令对象,避免上下文干扰:
# 替换旧函数的赋值语句,获取原始Cmdlet $oldInvoke = Get-Command Invoke-RestMethod -CommandType Cmdlet function _InvokeWithoutCache { Param($Uri, $Method, $Headers) # 调用原始Cmdlet的Invoke方法 $oldInvoke.Invoke(@($Uri), @{ Method = $Method; Headers = $Headers }) }
修正其他错误
同时修复GetFilename里的变量名错误:
function GetFilename { Param($Uri, $Method, $Headers) $md5 = New-Object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider # 修正$Header为$Headers $bytes = [Text.Encoding]::ASCII.GetBytes($Uri + $Method + $Headers) return [System.BitConverter]::ToString($md5.ComputeHash($bytes)) }
内容的提问来源于stack exchange,提问作者user25470
相关产品推荐
相关产品推荐

