How can I determine the parameters that were bound in just the current pipeline step?

懵懂的女人 提交于 2019-12-07 18:50:28

问题


Consider the following script:

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

Running the script results in the following output:

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

This seems to reveal that both $PSBoundParameters and $PSCmdlet.MyInvocation.BoundParameters contain the cumulation of all the parameters bound so far.

I'm fairly sure that $x and $z were bound on the first step, and $x and $y were bound on the second step, but I haven't found a way to retrieve that detail programmatically.

How can I determine the parameters that were bound in just the current pipeline step?


Why do I care about this? Some kinds of parameter validation are more complex than can be achieved using language features like parameter sets, ValidateScript(), etc. That validation has to be performed inside the function body. Occasionally it is desirable to consider an unbound parameter to be semantically different from passing $null to that same parameter. Detection of bound parameters is customarily achieved by interrogating $PSBoundParameters. This works fine if you pass only a single parameter object on the pipeline. However, if you pipe a list of parameter objects using the pipeline, that detection is foiled because of the problem demonstrated by the script above. This is violates the principle-of-least surprise because a function that worked fine inside a foreach loop behaves dramatically differently when the caller happens to invoke it by piping those same objects to it.

I can work around this by calling the affected functions from within a foreach, but I'd rather solve the problem for all invocations rather than abandon the pipeline altogether.


回答1:


You can remember all parameters bounded by command line in begin block, then in the end of process block you can clean up $PSBoundParameters from parameters bounded by current input object:

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($_) }
    }
}


来源:https://stackoverflow.com/questions/34032902/how-can-i-determine-the-parameters-that-were-bound-in-just-the-current-pipeline

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!