Powershell参数名称空间冲突

时间:2013-02-09 20:02:21

标签: powershell

我是Powershell初学者,虽然不是编程n00b。我正在尝试创建一个IDisposable / RAII风格的故障安全模式,有点像:

http://www.sbrickey.com/Tech/Blog/Post/IDisposable_in_PowerShell

所以我有:

Function global:FailSafeGuard
{
param (
[parameter(Mandatory=$true)] [ScriptBlock] $execute,
[parameter(Mandatory=$true)] [ScriptBlock] $cleanup
)

    Try { &$execute }
    Finally { &$cleanup }
}

我正在尝试使用它在不同的目录中执行一系列任务,在路上使用Push-Location,在路上使用Pop-Location。所以我有:

Function global:Push-Location-FailSafe
{
param (
$location,
[ScriptBlock] $execute
)
    FailSafeGuard {
        Push-Location $location;
        &$execute
        } { Pop-Location }  
}

我发现Push-Location-FailSafe中的$ execute参数与FailSafe函数中的$ execute参数冲突。

Push-Location-FailSafe "C:\" {dir}
The expression after '&' in a pipeline element produced an invalid object. It must result in a command name, script block or CommandInfo object.
At C:\TEMP\b807445c-1738-49ff-8109-18db972ab9e4.ps1:line:20 char:10
+         &$ <<<< execute

我认为这是名称冲突的原因是,如果我在Push-Location-FailSafe中将$ execute重命名为$ execute2,它可以正常工作:

Push-Location-FailSafe "C:\" {dir}
    Directory: C:\

Mode                LastWriteTime     Length Name   
----                -------------     ------ ----   
d----        2011-08-18     21:34            cygwin 
d----        2011-08-17     01:46            Dell   
[snip]

我对参数的理解有什么问题?

1 个答案:

答案 0 :(得分:1)

你的问题在于scriptblocks以及它们如何处理变量。脚本块中的变量在执行之前不会展开。因此,你正在打一个循环。让我告诉你:

当您调用Push-Location-Failsafe方法时,您的变量就是这样:

[DBG]: PS C:\>> (Get-Variable execute).Value
 dir 

然后你调用内部函数FailSafeGuard,你的$execute变量改为:

[DBG]: PS C:\>> (Get-Variable execute).Value

        Push-Location $location;
        & $execute

现在当你try { }块开始执行时,它开始扩展变量。当它展开$execute时,它将如下所示:

Try { 
    Push-Location $location;
    & $execute 
}

然后再次展开$execute。您的尝试块现在是:

Try { 
    Push-Location $location;
    & {
        Push-Location $location;
        & $execute
      } 
}

你得到了一个由递归引起的无限循环。要解决此问题,您可以在字符串中展开$execute变量,然后创建一个脚本块。像这样:

Function global:Push-Location-FailSafe
{
param (
$location,
[ScriptBlock] $execute
)
    FailSafeGuard ([ScriptBlock]::Create("
        Push-Location $location;
        & $execute")) { Pop-Location }  
}

请注意,当$execute包含变量时,此特定解决方案会出现问题。例如:$execute = { $h = dir }因为它会在创建脚本块时尝试扩展$ h。

更简单,更好的方法解决它只是使用不同的变量名,所以首先没有碰撞: - )