"Parameter set cannot be resolved" 当管道参数与其他参数的特定组合时

"Parameter set cannot be resolved" when piping a parameter with specific combinations of other parameters

我写了一个使用四个参数和四个参数集的函数。第一个参数 $Path 未分配给集合,因此属于所有集合。它也是强制性的,并且是唯一可以从管道传递的参数。但是,当我在管道末端调用函数时使用其他三个参数的某些组合(所有这些参数都属于四个集合的某些组合)执行此操作时,我收到一条错误消息,指示该集合不明确。

这是我的函数:

function Foo-Bar {
    [CmdletBinding(DefaultParameterSetName = 'A')]

    param (
        [Parameter(Mandatory = $true,
            ValueFromPipeline = $true)]
        [ValidateNotNullOrEmpty()]
        [string[]] $Path,

        [Parameter(ParameterSetName = 'A')]
        [Parameter(ParameterSetName = 'A-Secure')]
        [Switch] $OutputToConsole,

        [Parameter(Mandatory = $true,
            ParameterSetName = 'B')]
        [Parameter(Mandatory = $true,
            ParameterSetName = 'B-Secure')]
        [int] $OutputMode,

        [Parameter(Mandatory = $true,
            ParameterSetName = 'A-Secure')]
        [Parameter(Mandatory = $true,
            ParameterSetName = 'B-Secure')]
        [Switch] $Login
    )

    $PSCmdlet.ParameterSetName
}

所有可能的参数组合如下:

PS C:\> Foo-Bar -Path "C:\Test.jpg"
A
PS C:\> Foo-Bar -Path "C:\Test.jpg" -OutputToConsole
A
PS C:\> Foo-Bar -Path "C:\Test.jpg" -OutputToConsole -Login
A-Secure
PS C:\> Foo-Bar -Path "C:\Test.jpg" -Login
A-Secure
PS C:\> Foo-Bar -Path "C:\Test.jpg" -OutputMode 1
B
PS C:\> Foo-Bar -Path "C:\Test.jpg" -OutputMode 1 -Login
B-Secure

单独通过管道传递 $Path,或者与这些其他参数组合一起传递都可以正常工作:

PS C:\> "C:\Test.jpg" | Foo-Bar
A
PS C:\> "C:\Test.jpg" | Foo-Bar -OutputToConsole
A
PS C:\> "C:\Test.jpg" | Foo-Bar -OutputToConsole -Login
A-Secure
PS C:\> "C:\Test.jpg" | Foo-Bar -OutputMode 1 -Login
B-Secure

但是这两个组合会导致错误:

PS C:\> "C:\Test.jpg" | Foo-Bar -Login
Foo-Bar: Parameter set cannot be resolved using the specified named parameters. One or more parameters issued cannot be used together or an insufficient number of parameters were provided.
PS C:\> "C:\Test.jpg" | Foo-Bar -OutputMode 1
Foo-Bar: Parameter set cannot be resolved using the specified named parameters. One or more parameters issued cannot be used together or an insufficient number of parameters were provided.

这两个结果之间最大的区别似乎是$OutputToConsole,唯一一个在两个集合中都是可选的参数。似乎管道强制参数会导致它本身成为强制参数。另一方面,最令人困惑的结果涉及 $OutputMode,因为它的两个集合都使用完全强制参数的不同组合。同时使用 $Path$OutputMode 时会出现集合 B,仅此而已。那么 "C:\Test.jpg" | Foo-Bar -OutputMode 1 是如何被认为是模棱两可的呢?

如果有人能为我阐明这一点,我将不胜感激。

参数集必须包含至少一个唯一参数

Declaring parameter sets To create a parameter set, you must specify the ParameterSetName keyword of the Parameter attribute for every parameter in the parameter set. For parameters that belong to multiple parameter sets, add a Parameter attribute for each parameter set.

The Parameter attribute enables you to define the parameter differently for each parameter set. For example, you can define a parameter as mandatory in one set and optional in another. However, each parameter set must contain at least one unique parameter.

Parameters that don't have an assigned parameter set name belong to all parameter sets.

来源:Declaring Parameters Sets


更新 - 仔细观察

Foo-Bar

ParameterSetName Parameters
---------------- ----------
A                -Path <string[]> [-OutputToConsole] [<CommonParameters>]
A-Secure         -Path <string[]> -Login [-OutputToConsole] [<CommonParameters>]
B-Secure         -Path <string[]> -OutputMode <int> -Login [<CommonParameters>]
B                -Path <string[]> -OutputMode <int> [<CommonParameters>]

当你进入

C:\Test.jpg" | Foo-Bar -Login

PoSH 无法确定它应该与 A-Secure 还是 B-Secure 一起使用。在 B-Secure 中你有一个额外的强制参数(-OutputMode)并不重要。从收到的内容中它无法判断要选择哪个,因为有多个匹配选择。该组合不是唯一的。

"C:\Test.jpg" | Foo-Bar -OutputMode 1

此处相同,只有 OutputMode。它收到了 Path 和 OutputMode 参数,但这同时匹配 B-Secure 和 B。不唯一。

Microsoft 测量线示例

ParameterSetName Parameters
---------------- ----------
Path             [-Path] <string[]> [-Lines] [-Words] [-Characters] [-Recurse] [<CommonParameters>]
PathAll          [-Path] <string[]> -All [-Recurse] [<CommonParameters>]
LiteralPath      -LiteralPath <string> [-Lines] [-Words] [-Characters] [<CommonParameters>]
LiteralPathAll   -LiteralPath <string> -All [<CommonParameters>]

乍一看,Measure-Lines 示例似乎很相似,但不同之处在于,前 2 个参数集采用 Path 参数,而后 2 个参数集采用 LiteralPath 参数。这使得它们足够独特,Powershell 可以知道在使用 -All 开关时使用什么参数集。当与 -Path 参数一起使用时,它与 PathAll 集一起使用,并且在提供 -LiteralPath 时使用 LiteralPathAll。

别问我为什么.
(returns 下的解决方法与以下语法相同:Foo-Bar -?
就个人而言,我发现参数集非常混乱和冗长(因此我这样做是为了 Hierarchical Parameter Scripting #13746

无论如何,作为一种可能的解决方法;将 Path 参数放入所有参数集中:

function Foo-Bar {
    [CmdletBinding(DefaultParameterSetName = 'A')]

    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'A')]
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'B')]
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'A-Secure')]
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'B-Secure')]
        [ValidateNotNullOrEmpty()]
        [string[]] $Path,

        [Parameter(ParameterSetName = 'A')]
        [Parameter(ParameterSetName = 'A-Secure')]
        [Switch] $OutputToConsole,

        [Parameter(Mandatory = $true, ParameterSetName = 'B')]
        [Parameter(Mandatory = $true, ParameterSetName = 'B-Secure')]
        [int] $OutputMode,

        [Parameter(Mandatory = $true, ParameterSetName = 'A-Secure')]
        [Parameter(Mandatory = $true, ParameterSetName = 'B-Secure')]
        [Switch] $Login
    )

    $PSCmdlet.ParameterSetName
}

"C:\Test.jpg" | Foo-Bar -Login
A-Secure

跟随,你也可以在这里大幅减少参数集的数量我认为:

function Foo-Bar {
    [CmdletBinding(DefaultParameterSetName = 'None')]

    param (
        [ValidateNotNullOrEmpty()]
        [string[]] $Path,

        [Parameter(ParameterSetName = 'A-Secure')]
        [Switch] $OutputToConsole,

        [Parameter(Mandatory = $true, ParameterSetName = 'B-Secure')]
        [int] $OutputMode,

        [Parameter(Mandatory = $true, ParameterSetName = 'A-Secure')]
        [Parameter(Mandatory = $true, ParameterSetName = 'B-Secure')]
        [Switch] $Login
    )

    $PSCmdlet.ParameterSetName
}

Foo-Bar -Login                                            # --> chosen param set: 'A-Secure' because that the first one mentioned on the Login parameter
Foo-Bar -Path 'D:\Test\blah.txt'                          # --> chosen param set: 'None' because that is the DefaultParameterSetName
Foo-Bar -Path 'D:\Test\blah.txt' -OutputMode 5            # --> prompts you to also supply parameter 'Login'
Foo-Bar -Path 'D:\Test\blah.txt' -OutputToConsole -Login  # --> parameter -OutputMode now not available; chosen param set: 'B-Secure'
Foo-Bar -Path 'D:\Test\blah.txt' -OutputMode 5 -Login     # --> parameter -OutputToConsole now not available; chosen param set: 'A-Secure'

# parameters '-Path' and '-Login' are always available for both 'A-Secure' and 'B-Secure'