简体   繁体   English

PowerShell管道参数绑定顺序

[英]PowerShell pipeline parameter binding order

I have an advanced function that can accept two kinds of pipeline data: 我有一个高级函数,可以接受两种管道数据:

  • A custom object with a PSTypeName of "MyType" PSTypeName为“MyType”的自定义对象
  • Any object with an ID property 任何具有ID属性的对象

Here's my function: 这是我的功能:

function Test-PowerShell {
    [CmdletBinding(DefaultParameterSetName = "ID")]
    param (
        [Parameter(
            Mandatory = $true,
            ParameterSetName = "InputObject",
            ValueFromPipeline = $true
        )]
        [PSTypeName('MyType')]
        $InputObject,

        [Parameter(
            Mandatory = $true,
            ParameterSetName = 'ID',
            ValueFromPipelineByPropertyName = $true
            )]
        [int]
        $ID
    )

    process {
        if ($InputObject) {
            $objects = $InputObject
            Write-Verbose 'InputObject binding'
        }
        else {
            $objects = Get-MyType -ID $ID
            Write-Verbose 'ID binding'
        }

        # Do something with $objects
    }
}

I can use this function like this: 我可以像这样使用这个函数:

$obj = [PSCustomObject]@{
    PSTypeName = 'MyType'
    ID = 5
    Name = 'Bob'
}
$obj | Test-PowerShell -Verbose

Note that this object satisfies both of the above conditions: It is a MyType, and it has an ID property. 请注意,此对象满足上述两个条件:它是MyType,并且具有ID属性。 In this case, PowerShell always binds to the ID property. 在这种情况下,PowerShell始终绑定到ID属性。 This isn't ideal performance-wise because the piped object is discarded and I have to re-query it using the ID. 这在性能方面并不理想,因为管道对象被丢弃,我不得不使用ID重新查询它。 My question is this: 我的问题是:

How do I force PowerShell to bind the pipeline to $InputObject if possible? 如果可能,如何强制PowerShell将管道绑定到$ InputObject?

If I change the default parameter set to InputObject, PowerShell binds on $InputObject. 如果我将默认参数设置更改为InputObject,PowerShell将绑定$ InputObject。 I don't want this, however, because when run without parameters, I want PowerShell to prompt for an ID, not an InputObject. 但是,我不希望这样,因为在没有参数的情况下运行时,我希望PowerShell提示输入ID,而不是InputObject。

Simple answer: remove the Mandatory argument to the Parameter attribute on $InputObject to get the functionality you want. 简单的答案:删除$InputObject上的Parameter属性的Mandatory参数以获得所需的功能。 I don't have enough knowledge on how parameter binding works to explain why this works. 我没有关于如何参数绑定作品解释为什么这个作品足够的知识。

function Test-PowerShell {
    [CmdletBinding(DefaultParameterSetName = 'ID')]
    param(
        [Parameter(ParameterSetName = 'InputObject', ValueFromPipeline)]
        [PSTypeName('MyType')]
        $InputObject,

        [Parameter(ParameterSetName = 'ID', Mandatory, ValueFromPipelineByPropertyName)]
        [int]
        $ID
    )

    process {
        $PSBoundParameters
    }
}

$o = [pscustomobject]@{
    PSTypeName = 'MyType'
    ID         = 6
    Name       = 'Bob'
}


PS> $o | Test-PowerShell

Key         Value
---         -----
InputObject MyType


PS> [pscustomobject]@{ID = 6} | Test-PowerShell

Key Value
--- -----
ID      6

Thoughts and experimentation below. 下面的想法和实验。

Here's a workaround to your problem (defining your own type): 解决您的问题方法(定义您自己的类型):

Add-Type -TypeDefinition @'
public class MyType
{
    public int ID { get; set; }
    public string Name { get; set; }
}
'@

And then you would tag your parameter as [MyType] , creating objects like you would from [pscustomobject] : 然后你将你的参数标记为[MyType] ,创建像[pscustomobject]那样的对象:

[MyType]@{ ID = 6; Name = 'Bob' }

In hindsight, this method does not work. 事后看来,这种方法不起作用。 What you're running into is the behavior of the DefaultParameterSet . 您遇到的是DefaultParameterSet的行为。 I'd suggest changing what you take as pipeline input. 我建议你改变你的管道输入。 Is there a use-case for taking the ID as pipeline input versus a user just using Test-PowerShell -ID 5 or Test-PowerShell and being prompted for the ID? 是否存在将ID作为管道输入而不是仅使用Test-PowerShell -ID 5Test-PowerShell并被提示输入ID的用户的用例?

Here's a suggestion that may work as you intend from my testing: 这是一个可能与我的测试有关的建议:

function Test-PowerShell {
    [CmdletBinding(DefaultParameterSetName = 'ID')]
    param(
        [Parameter(ParameterSetName = 'InputObject', Mandatory = $true, ValueFromPipeline = $true)]
        [PSTypeName('MyType')]
        $InputObject,

        [Parameter(ParameterSetName = 'ID', Mandatory = $true, ValueFromPipeline = $true)]
        [int]
        $ID
    )

    process {
        $PSBoundParameters
    }
}

To take an example from an existing built-in cmdlet, they don't use the same name or properties on an object for multiple parameters. 要从现有的内置cmdlet中获取示例,它们不会在对象上使用相同的名称或属性来表示多个参数。 In Get-ChildItem , both the LiteralPath and Path take pipeline input, but LiteralPath only takes it by PropertyName LiteralPath or PSPath (aliased). Get-ChildItemLiteralPathPath接受管道输入,但LiteralPath只接受PropertyName LiteralPathPSPath (别名)。 Path is ByValue and PropertyName, but only as Path . Path是ByValue和PropertyName,但仅作为Path

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM