PowerShell关闭中的捕获功能

时间:2013-05-25 02:36:02

标签: function powershell closures

PowerShell闭包似乎没有捕获函数的定义:

PS C:\> function x() { Write-Host 'original x' }
PS C:\> function x-caller-generator() { return { Write-host 'Calling x!'; x }.GetNewClosure() }
PS C:\> $y = x-caller-generator
PS C:\> & $y
Calling x!
original x
PS C:\> function x() { Write-Host 'new x' }
PS C:\> & $y
Calling x!
new x

有没有办法捕捉函数的定义?

我实际遇到的是我创建了一个闭包,但是当我执行闭包时,该函数以某种方式超出了范围。 (构建脚本的psake模块正在做一些奇怪的事情。)像这样:

PS C:\> function closure-maker () {
>>     function x() { Write-Host 'x!' }
>>
>>     return { Write-host 'Calling x'; x }.GetNewClosure()
>> }
>>
PS C:\> $y = closure-maker
PS C:\> & $y
Calling x
The term 'x' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:3 char:39
+     return { Write-host 'Calling x'; x <<<<  }.GetNewClosure()
    + CategoryInfo          : ObjectNotFound: (x:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

注意:使用PowerShell 2.0,但如果有新内容,则对3.0答案感兴趣。

2 个答案:

答案 0 :(得分:3)

好吧,我发现至少可以用于简单功能的东西。我们可以使用Get-Item来获取描述该函数的对象,然后将原始脚本拉出来。像这样:

function x-caller-generator() {
    $xFunc = [ScriptBlock]::Create((Get-Item function:x).Definition)
    return { Write-host 'Calling x!'; & $xFunc }.GetNewClosure()
}

如果函数从未重新定义(比如我的函数超出范围的示例),我们可以避免取消定义并直接使用函数对象:

function closure-maker () {
    function x() { Write-Host 'x!' }

    $xFunc = Get-Item function:x
    return { Write-host 'Calling x'; & $xFunc }.GetNewClosure()
}

如果在执行闭包之前重新定义函数(至少在与原始函数相同的作用域中),则第二种方法将工作。对象显然是动态的;它跟踪当前的定义。

我严重怀疑这可以使用引用其他用户定义的函数的函数,这些函数也可能超出范围,但我的用例并不需要。

示例输出:

创建脚本块

PS C:\> function x() { Write-Host 'original x' }
PS C:\> function x-caller-generator() { $xFunc = [ScriptBlock]::Create((Get-Item function:x).Definition); return { Write-host 'Calling x!'; & $xFunc }.GetNewClosure() }
PS C:\> $y = x-caller-generator
PS C:\> & $y
Calling x!
original x
PS C:\> function x() { Write-Host 'new x' }
PS C:\> & $y
Calling x!
original x

使用功能对象

PS C:\> function closure-maker () {
>>     function x() { Write-Host 'x!' }
>>
>>     $xFunc = Get-Item function:x
>>     return { Write-host 'Calling x'; & $xFunc }.GetNewClosure()
>> }
>>
PS C:\> $y = closure-maker
PS C:\> & $y
Calling x
x!

尝试使用带有第一个示例的对象不起作用

PS C:\> function x() { Write-Host 'original x' }
PS C:\> function x-caller-generator() { $xFunc = Get-Item function:x; return { Write-host 'Calling x!'; & $xFunc }.GetNewClosure() }
PS C:\> $y = x-caller-generator
PS C:\> & $y
Calling x!
original x
PS C:\> function x() { Write-Host 'new x' }
PS C:\> & $y
Calling x!
new x

答案 1 :(得分:0)

小的修正会让第一个例子起作用:

clear
function x() { Write-Host 'original x' }
function x-caller-generator() { 
      $xFunc = $function:x; # instead of Get-Item
      return { Write-host 'Calling x!'; & $xFunc }.GetNewClosure() 
}
$y = x-caller-generator
& $y
function x() { Write-Host 'new x' }
& $y

输出:

Calling x!
original x
Calling x!
original x

PowerShell有太多外观相似,实际上表现不同。您可以使用$ function前缀获取函数对象。有人可能会认为它与Get-Item的工作方式相同,但它没有......