PowerShell管道参数绑定顺序

Dav*_*ond 5 parameters powershell typing

我有一个高级函数,可以接受两种管道数据:

  • PSTypeName 为“MyType”的自定义对象
  • 任何具有 ID 属性的对象

这是我的功能:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

我可以这样使用这个函数:

$obj = [PSCustomObject]@{
    PSTypeName = 'MyType'
    ID = 5
    Name = 'Bob'
}
$obj | Test-PowerShell -Verbose
Run Code Online (Sandbox Code Playgroud)

请注意,该对象满足上述两个条件:它是 MyType,并且具有 ID 属性。在这种情况下,PowerShell 始终绑定到 ID 属性。这在性能方面并不理想,因为管道对象被丢弃,我必须使用 ID 重新查询它。我的问题是这样的:

如果可能的话,如何强制 PowerShell 将管道绑定到 $InputObject?

如果我将默认参数集更改为 InputObject,PowerShell 将绑定到 $InputObject。然而,我不希望这样,因为当不带参数运行时,我希望 PowerShell 提示输入 ID,而不是 InputObject。

The*_*le1 3

简单的答案:删除on 属性Mandatory的参数以获得您想要的功能。我对参数绑定的工作原理没有足够的了解,无法解释其工作原理。Parameter$InputObject

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
Run Code Online (Sandbox Code Playgroud)

下面的想法和实验。

这是解决您的问题的方法(定义您自己的类型):

Add-Type -TypeDefinition @'
public class MyType
{
    public int ID { get; set; }
    public string Name { get; set; }
}
'@
Run Code Online (Sandbox Code Playgroud)

然后你将你的参数标记为[MyType],创建对象就像你从[pscustomobject]:

[MyType]@{ ID = 6; Name = 'Bob' }
Run Code Online (Sandbox Code Playgroud)

事后看来,这个方法行不通。您遇到的是DefaultParameterSet. 我建议更改您作为管道输入的内容。Test-PowerShell -ID 5是否存在将 ID 作为管道输入而不是用户仅使用或Test-PowerShell并被提示输入 ID的用例?

根据我的测试,以下是一个可能符合您预期的建议:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

以现有内置 cmdlet 为例,它们不会在对象上为多个参数使用相同的名称或属性。在 中Get-ChildItem, 和LiteralPath都Path采用管道输入,但仅通过 PropertyName或(别名)LiteralPath进行输入。 是 ByValue 和 PropertyName,但仅作为.LiteralPathPSPathPathPath