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

Powershell 如何确定仅在当前管道步骤中绑定的参数?,powershell,pipeline,parameterbinding,Powershell,Pipeline,Parameterbinding,考虑以下脚本: function g { [CmdletBinding()] param ( [parameter(ValueFromPipelineByPropertyName = $true)]$x, [parameter(ValueFromPipelineByPropertyName = $true)]$y, [parameter(ValueFromPipelineByPropertyName = $true)]$z

考虑以下脚本:

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
中调用受影响的函数来解决这个问题,但我宁愿解决所有调用的问题,也不愿完全放弃管道。

您可以记住
begin
块中由命令行限定的所有参数,然后在
过程
块的末尾,您可以从由当前输入对象限定的参数中清除
$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($_) }
    }
}

这是一个非常有趣的问题。你的最终目标是什么?还可以尝试使用
Trace命令
,看看它是否揭示了有关绑定过程的任何信息。@briantist我对这个问题做了解释。这似乎会起作用。我做了一些正式的CI测试。这些测试在PowerShell 2、4和5下通过。谢谢你的帮助!:)很好的纠缠测试@alx9r!:)谢谢@briantist。