1

我是 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]

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

4

1 回答 1

1

您的问题在于脚本块以及它们如何处理变量。脚本块中的变量在执行之前不会扩展。正因为如此,你正在打一个循环。我来给你展示:

当你调用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。

解决它的一种更简单更好的方法是使用不同的变量名,这样一开始就没有冲突:-)

于 2013-02-09T21:20:34.730 回答