【问题标题】:PowerShell : GetNewClosure() and Cmdlets with validationPowerShell : GetNewClosure() 和带有验证的 Cmdlet
【发布时间】:2018-12-06 18:21:55
【问题描述】:

我试图了解 .GetNewClosure() 如何在 PowerShell 2 中的脚本 cmdlet 上下文中工作。

本质上,我有一个返回对象的函数,如下所示:

function Get-AnObject {
param(
    [CmdletBinding()]
    [Parameter(....)]
    [String[]]$Id
    ..
    [ValidateSet('Option1','Option2')]
    [String[]]$Options
)

...

    $T = New-Object PSCustomObject -Property @{ ..... } 
    $T | Add-Member -MemberType ScriptProperty -Name ExpensiveScriptProperty -Value {
        $this | Get-ExpensiveStuff
    }.GetNewClosure() 

..
}

如果我没有验证集选项,则关闭似乎可以正常工作。如果包含它,则新的闭包会失败并出现以下错误。

使用“0”参数调用“GetNewClosure”的异常:“无法添加属性,因为它会导致具有值的变量选项变得无效。”

大概是闭包试图捕获调用 Cmdlet 的上下文。由于参数“Options”根本没有绑定,这对参数验证来说不是很好。

我想可以通过将验证作为代码放置在 Cmdlet 的主体中来避免这种情况,而不是使用 [Validate*()] 装饰器——但这看起来很讨厌而且很晦涩。有没有办法融合这两个想法?

【问题讨论】:

    标签: validation powershell closures powershell-2.0 powershell-cmdlet


    【解决方案1】:

    “无法添加属性”消息是(或曾经是)PowerShell 错误,我已使用 this bug report 将其提交给 Microsoft。该特定问题似乎已得到修复,(可能在 V5.1 左右。但任何对 Powershell 闭包感兴趣的人可能仍然会发现以下有趣的信息。

    有一种在早期版本中有效的解决方法,但首先这里有一个简化的重现案例,它会产生相同的错误:

    function Test-ClosureWithValidation {
        [CmdletBinding()]
        param(
            [Parameter()]
            [ValidateSet('Option1','Option2')]
            [String[]]$Options
        )
        [scriptblock] $closure = {"OK"}.GetNewClosure();
        $closure.Invoke()
    }
    
    Test-ClosureWithValidation -Options Option1
    

    解决方法取决于 GetNewClosure() 通过迭代调用脚本上下文中的局部变量,将这些局部变量绑定到脚本上下文中的事实。出现错误是因为它复制了包含验证属性的 $Options 变量。您可以通过仅使用所需的局部变量创建新上下文来解决该错误。在上面的简单复制中,这是一种单行解决方法:

        [scriptblock] $closure = &{ {"OK"}.GetNewClosure();}
    

    上面的行现在创建了一个没有局部变量的作用域。对于您的情况,这可能太简单了;如果你需要一些来自外部作用域的值,你可以将它们复制到新作用域的局部变量中,例如:

        [scriptblock] $closure = &{ 
            $options = $options; 
            {"OK $options"}.GetNewClosure();
        }
    

    请注意,上面的第二行创建了一个新的 $options 变量,并为其分配了外部变量的 ,属性不会传播。

    最后,在您的示例中,我不确定您为什么需要调用 GetNewClosure。变量 $this 不是普通的局部变量,无论您是否创建闭包,它都将在您的脚本属性中可用。示例:

    function Test-ScriptPropertyWithoutClosure {
        [CmdletBinding()]
        param(
            [Parameter()]
            [ValidateSet('Option1','Option2')]
            [String[]]$Options
        )
        [pscustomobject]@{ Timestamp= Get-Date} | 
            Add-Member ScriptProperty ExpensiveScriptProperty { 
                $this | get-member -MemberType Properties| % Name 
            } -PassThru
    }
    
    Test-ScriptPropertyWithoutClosure -Options Option1 | fl
    

    【讨论】:

    • 伟大的侦探;我已经更新了错误报告的链接,以指向它在 windowsserver.uservoice.com 的新主页——然而,不幸的是,它在那里被错误地标记为“已完成”,即使问题在 PowerShell v5.1.14393 中仍然存在。 693 / PowerShell 核心 v6.0.0-alpha.15。因此,在撰写本文时,您无法对错误报告进行投票;您只能发表评论。
    • 谢谢@mklement0,很高兴你发现这很有帮助。我运行了我为 5.1.14393.693 下的错误报告编写的测试用例,输出了预期的结果(“OK”),没有错误,这可能是它被标记为已完成的原因。如果对此有错误恢复使用 PowerShell Core 6,最好打开一个新问题,而不是回收旧问题。
    • 是的,您的测试用例通过了,但我希望它总是通过,因为它通过了 valid -Options 值。当您 传递任何 -Options 值时,问题 - 错误 - 就会出现 - 无论是在 Windows PowerShell 和 PowerShell Core 中。但我明白你关于打开一个新问题的观点:你认为在哪里发布更有意义:windowsserver.uservoice.com 或开源 GitHub 项目?
    • 好的,听起来我的错误修复并没有改变 GetNewClosure() 只捕获局部变量(而不是参数)的事实。如果是这样,我提到的另一个解决方法 [putting GetNewClosure () 在脚本块中,参数重新分配给局部变量] 可能仍然需要。我认为提交它的任何一个地方都可以工作,但在内部它可能被解决为“设计”,所以使用解决方法可能是你最好的选择。更改可能会破坏现有脚本。
    • 如果我理解错了,可能最好写一个新的测试用例和stackoverflow线程。我不再为微软工作,但如果你在文中@提到我,我会看看它。
    【解决方案2】:

    我相信这可能会奏效:

    function Get-AnObject {
    param(
          [CmdletBinding()]
          [Parameter(....)]
          [String[]]$Id
          ..
          [ValidateSet('Option1','Option2')]
          [String[]]$Options
        )
    
    ...
    $sb = [scriptblock]::create('$this | Get-ExpensiveStuff')
    $T = New-Object PSCustomObject -Property @{ ..... } 
    $T | Add-Member -MemberType ScriptProperty -Name ExpensiveScriptProperty -Value $sb 
    
    .. }
    

    这会将脚本块的创建延迟到运行时。

    【讨论】:

      猜你喜欢
      • 2010-12-05
      • 1970-01-01
      • 1970-01-01
      • 2013-09-16
      • 2021-10-20
      • 1970-01-01
      • 2017-12-31
      • 2012-02-26
      • 1970-01-01
      相关资源
      最近更新 更多