我是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]
我对参数的理解有什么问题?
答案 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。
更简单,更好的方法解决它只是使用不同的变量名,所以首先没有碰撞: - )