参数 - 需要一些但不需要其他 - 参数集的正确使用

Parameters - Requiring Some But Not Others - Correct Use of Parameter Sets

所以我正在尝试使用 PowerShell,但在理解参数方面遇到了一些困难。根据我的阅读,如果我指定一个参数与另一个参数位于同一位置,但将其放在单独的 ParameterSet 中,PowerShell 将只需要这些参数之一。

在这个例子中按预期工作 -

[CmdletBinding(DefaultParameterSetName='MultiUser')]

Param(
[Parameter(Mandatory=$True,Position=1)]
[string]$Token,

[Parameter(Mandatory=$True,Position=2, ParameterSetName="MultiUser")]
[string]$UsernamesFile,

[Parameter(Mandatory=$True,Position=2, ParameterSetName="SingleUser")]
[string]$SingleUsername,

[Parameter(Mandatory=$False)]
[switch]$SpecialCase
)

但是如果我想对此进行扩展,那么您必须指定一个令牌,然后您必须指定一个用户名 用户名文件,但现在我想指定用户所在的组。

现在假设用户必须进入两个组之一,我不想担心处理用户可能输入组名的不同方式,所以我使用两个开关。我只希望用户在一个组中而不是两个组中,因此开关应该在相同的位置但在不同的参数集中(基于我读过的内容和上面的示例有效)。

所以我的第二个例子是这样的 -

[CmdletBinding(DefaultParameterSetName='MultiUser')]
Param(
[Parameter(Mandatory=$True,Position=1)]
[string]$Token,

[Parameter(Mandatory=$True,Position=2, ParameterSetName="MultiUser")]
[string]$UsernamesFile,

[Parameter(Mandatory=$True,Position=2, ParameterSetName="SingleUser")]
[string]$SingleUsername,

[Parameter(Mandatory=$True,Position=3, ParameterSetName="GroupA")]
[switch]$GroupA,

[Parameter(Mandatory=$True,Position=3, ParameterSetName="GroupB")]
[switch]$GroupB,

[Parameter(Mandatory=$False)]
[switch]$SpecialCase
)

然而,这并没有按预期工作,这给了我一个错误 -

有人可以解释为什么这不起作用并纠正我对 PowerShell 参数的理解吗?

谢谢!

我会合并这两个开关并根据您希望它们在哪个组中进行输入。

您可以使用 ValidateSet 验证属性来确保用户指定两个特定组之一

Param(
    [Parameter(Mandatory=$True,Position=1)]
    [string]$Token,

    [Parameter(Mandatory=$True,Position=2, ParameterSetName="MultiUser")]
    [string]$UsernamesFile,

    [Parameter(Mandatory=$True,Position=2, ParameterSetName="SingleUser")]
    [string]$SingleUsername,

    [Parameter(Mandatory=$True,Position=3)]
    [ValidateSet('A','B')]
    [string]$Group,

    [Parameter(Mandatory=$False)]
    [switch]$SpecialCase
)

见底部以了解 OP 方法问题的解释。

得到您想要的,您必须使用以下内容:

# - Make sure that parameters are NON-positional unless explicitly marked otherwise.
# - Specify the default parameter set.
[CmdletBinding(PositionalBinding=$False, DefaultParameterSetName='MultiUserA')]
Param(

  # Belongs to all parameter sets.
  [Parameter(Mandatory, Position=1)]
  [string]$Token,

  # Mandatory and positional both when combined with -GroupA or -GroupB.
  [Parameter(Mandatory, Position=2, ParameterSetName='MultiUserA')]
  [Parameter(Mandatory, Position=2, ParameterSetName='MultiUserB')]
  [string] $UsernamesFile,

  # Mandatory - but not positional - both when combined with -GroupA or -GroupB.
  [Parameter(Mandatory, ParameterSetName='SingleUserA')]
  [Parameter(Mandatory, ParameterSetName='SingleUserB')]
  [string] $SingleUsername,

  # Mandatory, whether combined with -UsernamesFile or -SingleUsername
  [Parameter(Mandatory, ParameterSetName='SingleUserA')]
  [Parameter(Mandatory, ParameterSetName='MultiUserA')]
  [switch] $GroupA,

  # Mandatory, whether combined with -UsernamesFile or -SingleUsername
  [Parameter(Mandatory, ParameterSetName='SingleUserB')]
  [Parameter(Mandatory, ParameterSetName='MultiUserB')]
  [switch] $GroupB,

  # Belongs to all parameter sets. Non-mandatory by default.
  [switch] $SpecialCase

)

如您所见,

  • 您需要定义 4 个参数集,这些参数集相当于所有 users-file-vs.-single-user 和 groupA-vs.-group-B 组合.

  • 您需要将每个参数分配给多个个参数集,选择合适的子集。

    • 请注意,每个 [Parameter(...)] 属性只能指定 1 参数集,以及您在那里指定的任何其他属性 - MandatoryPosition , ... - 仅在给定参数集 .
    • 的上下文中应用于参数

当您使用 -? 调用脚本(或将其传递给 Get-Help)时,您将看到生成的语法图:

script.ps1 [-Token] <string> [-UsernamesFile] <string> -GroupA [-SpecialCase] [<CommonParameters>]
script.ps1 [-Token] <string> [-UsernamesFile] <string> -GroupB [-SpecialCase] [<CommonParameters>]
script.ps1 [-Token] <string> -SingleUsername <string> -GroupB [-SpecialCase] [<CommonParameters>]
script.ps1 [-Token] <string> -SingleUsername <string> -GroupA [-SpecialCase] [<CommonParameters>]

但是,这种方法是不明智的,原因如下:

  • 你不应该有强制 [switch]参数,因为根据定义它们是可选 .

  • 当您为 interactive 参数条目调用不带参数的脚本时,PowerShell 不会让您指定开关值(我试过的值都不起作用: 不是 true, false, 1, 0, ... - 试试 ./script someToken someFile)

  • ./script.ps1 someToken -SingleUsername someUser 给出一般错误消息 (Parameter set cannot be resolved using the specified named parameters.) 而不是具体指出缺少 -GroupA-GroupB,因为 PowerShell不知道你是指参数集 SingleUserA 还是 SingleUserB.

    • 相比之下,./script someToken someFile - 隐含 -UsernamesFile - 是明确的,因为 MultiUserAdefault 参数集,但由于-GroupA 开关是 强制性的 ,您仍然会得到输入其值的提示。
  • 最后但同样重要的是,正如 Mathias R. Jessen 指出的那样,使用不同的、互斥的开关(-GroupA-GroupB)不能很好地扩展,因为快速添加更多 -Group* 开关会使必须在各自的参数集中反映的组合数量难以管理 - 请参阅下文了解如何避免这种情况。


正如 中指出的那样,更好的方法 是使用 单个参数 对于 只接受一组给定值中的一个值的目标组 [ValidationAttribute] 可以确保:

[CmdletBinding(PositionalBinding=$False, DefaultParameterSetName='MultiUser')]
Param(

  [Parameter(Mandatory, Position=1)]
  [string] $Token,

  [Parameter(Mandatory, Position=2, ParameterSetName='MultiUser')]
  [string] $UsernamesFile,

  [Parameter(Mandatory, ParameterSetName='SingleUser')]
  [string] $SingleUsername,

  # Single -Group parameter that only accepts values 'GroupA' and 'GroupB'
  # Input validation is case-INsensitive, as usual.
  [Parameter(Mandatory)]
  [ValidateSet('GroupA', 'GroupB')]
  [string] $Group,

  [switch] $SpecialCase

)

这给了我们下面的语法图(注意-Group的有效值集合是不是反映的):

script.ps1 [-Token] <string> [-UsernamesFile] <string> -Group <string> [-SpecialCase] [<CommonParameters>]
script.ps1 [-Token] <string> -SingleUsername <string> -Group <string> [-SpecialCase] [<CommonParameters>]
  • 这将所需参数集的数量减少到2

  • 它支持组名称的交互式输入(尽管有点不幸,提供无效名称中止调用)。

  • 如果省略 -Group./script.ps someToken someFile./script.ps someToken -SingleUserName someUser 现在的行为相同:它们提示输入 -Group 值。

  • 虽然必须键入 -Group 一个值在调用时可能比具有不同的开关 -GroupA-GroupB,

    • 如果组的数量随着时间的推移而增加,那么它是更可扩展的方法,
    • tab 完成确实可以扩展/循环显示有效值。

至于你原来的方法有问题

  • 正如 Clijsters 指出的那样,您尝试调用 ./script.ps1 -Token a -UsernamesFile someFile -GroupA 失败了,因为:

    • PowerShell 需要明确地将给定的参数组合解析为参数集。

    • -GroupA 只有属于参数集GroupA,而-UsersnameFile 只有属于参数集MultiUser,所以这些参数实际上互斥,PowerShell无法确定使用什么参数集

  • 所有非开关参数都是位置 默认 - 除非你用[=52=明确停用它] - 然后只有单独的 [Parameter(...)] 属性明确标记为 Position 属性成为位置。

  • 另外,[switch] 参数 positional 没有意义,因为根据定义它们是非位置的:你总是有指定它们的名称(明确),这样就可以将它们放置在任何地方。