【问题标题】:How can I determine the parameters that were bound in just the current pipeline step?如何确定仅在当前管道步骤中绑定的参数?
【发布时间】:2016-03-06 02:43:28
【问题描述】:

考虑以下脚本:

function g
{
    [CmdletBinding()]
    param
    (
        [parameter(ValueFromPipelineByPropertyName = $true)]$x,
        [parameter(ValueFromPipelineByPropertyName = $true)]$y,
        [parameter(ValueFromPipelineByPropertyName = $true)]$z
    )
    process
    {
        $retval = @{psbp=@{};mibp=@{};x=$x;y=$y;z=$z}
        $PSBoundParameters.Keys |
            % { $retval.psbp.$_ = $PSBoundParameters.$_ }
        $PSCmdlet.MyInvocation.BoundParameters.Keys |
            % { $retval.mibp.$_ = $PSCmdlet.MyInvocation.BoundParameters.$_} 
        return New-Object psobject -Property $retval
    }
}

$list = (New-Object psobject -Property @{x=1;z=3}),
        (New-Object psobject -Property @{x=$null;y=2} ) 
$list | 
    g |
    Select bp,x,y,z |
    ft -AutoSize

运行脚本会产生以下输出:

psbp      mibp      x y z
----      ----      - - -
{z, x}    {z, x}    1   3
{y, z, x} {y, z, x}   2 

这似乎表明$PSBoundParameters$PSCmdlet.MyInvocation.BoundParameters 都包含到目前为止绑定的所有参数的累积

我很确定 $x$z 在第一步绑定,$x$y 在第二步绑定,但我还没有找到检索该详细信息的方法以编程方式。

如何确定仅在当前管道步骤中绑定的参数?


我为什么要关心这个?某些类型的参数验证比使用参数集、ValidateScript() 等语言功能所能实现的更为复杂。该验证必须在函数体内执行。有时,需要考虑将未绑定参数与将$null 传递给同一参数的语义不同。绑定参数的检测通常通过询问$PSBoundParameters 来实现。如果您在管道上仅传递一个参数对象,则此方法可以正常工作。但是,如果您使用管道传递参数对象列表,则由于上述脚本演示的问题,该检测会失败。这违反了最小意外原则,因为在 foreach 循环中运行良好的函数在调用者碰巧通过管道传递相同的对象来调用它时,其行为会大不相同。

可以通过在foreach 中调用受影响的函数来解决此问题,但我宁愿解决所有调用的问题,也不愿完全放弃管道。

【问题讨论】:

  • 这是一个非常有趣的问题。你的最终目标是什么?还可以尝试使用Trace-Command,看看它是否可能揭示有关绑定过程的任何信息。
  • @briantist 我为问题添加了解释。

标签: powershell pipeline parameterbinding


【解决方案1】:

您可以记住begin 块中所有受命令行限制的参数,然后在process 块的末尾,您可以从当前输入对象限制的参数中清除$PSBoundParameters

function g
{
    [CmdletBinding()]
    param
    (
        [parameter(ValueFromPipelineByPropertyName = $true)]$x,
        [parameter(ValueFromPipelineByPropertyName = $true)]$y,
        [parameter(ValueFromPipelineByPropertyName = $true)]$z
    )
    begin
    {
        $CommandLineBoundParameters=@($PSBoundParameters.Keys)
    }
    process
    {
        ...

        @($PSBoundParameters.Keys) |
            ? { $CommandLineBoundParameters -notcontains $_ } |
            % { [void]$PSBoundParameters.Remove($_) }
    }
}

【讨论】:

  • 这似乎可行。我进行了一些正式的 CI 测试here。这些测试在 PowerShell 2、4 和 5 下通过。感谢您的帮助! :)
  • Nice Pester 测试 @alx9r !
  • :) 谢谢@briantist。
猜你喜欢
  • 2018-07-29
  • 2021-06-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-05
  • 2019-05-15
  • 1970-01-01
  • 2021-10-11
相关资源
最近更新 更多