参数 - 需要一些而不是其他 - 正确使用参数集

时间:2017-03-25 15:25:49

标签: powershell parameter-passing 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
)

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

New Parameters Can't Be Found

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

谢谢!

2 个答案:

答案 0 :(得分:3)

有关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 参数集,这些参数集相当于所有用户 - 文件与单用户和groupA-versus-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]参数,因为它们按照定义可选

    < / LI>
  • 当你调用没有交互式参数条目参数的脚本时,PowerShell不会让你指定一个开关值(没有值我尝试过工作:不是{ {1}},truefalse1,... - 尝试0

  • ./script someToken someFile提供了一般错误消息(./script.ps1 someToken -SingleUsername someUser),而不是明确指出缺少Parameter set cannot be resolved using the specified named parameters.-GroupA,因为PowerShell无法知道您是否表示参数集-GroupBSingleUserA

    • 相比之下,SingleUserB - 隐含./script someToken someFile - 是明确的,因为-UsernamesFile默认参数集,但由于{{1} }切换为强制,你仍然会得到它的值的提示。)
  • 最后,但并非最不重要的是,正如Mathias R. Jessen指出的那样,使用不同的,互斥的交换机(MultiUserA-GroupA)并不能很好地扩展,因为快速添加更多-GroupA个开关会使每个必须在自己的参数集中反映出来的组合数量无法管理 - 请参阅下文,了解如何避免这种情况。

正如Mathias R. Jessen's helpful answer所指出的,更好的方法是对的目标群组使用 单一参数 >仅接受来自给定值集合的值-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

)
  • 这会将所需参数集的数量减少到 2

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

  • 如果省略-Group,则script.ps1 [-Token] <string> [-UsernamesFile] <string> -Group <string> [-SpecialCase] [<CommonParameters>] script.ps1 [-Token] <string> -SingleUsername <string> -Group <string> [-SpecialCase] [<CommonParameters>] -Group现在的行为相同:它们会提示./script.ps someToken someFile值。

    < / LI>
  • 虽然必须键入./script.ps someToken -SingleUserName someUser ,但在调用时,值可能比使用不同的开关-Group-Group稍微麻烦一点,

    • 如果群体数量随着时间的推移而增长,则是更具可扩展性的方法,
    • 制表符完成功能可以扩展/循环显示有效值。

至于原始方法的问题

  • 正如Clijsters所指出的那样,您调用-GroupA的尝试失败了,原因是:

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

    • -GroupB 属于参数集./script.ps1 -Token a -UsernamesFile someFile -GroupA,而-GroupA 仅属于属于参数集{{1这些参数实际上是互斥的,而PowerShell无法确定要使用的参数集。

  • 默认情况下,所有非切换参数都是位置 - 除非您使用GroupA明确停用 - 但只有-UsersnameFile个人MultiUser明确标有[CmdletBinding(PositionalBinding=$False,...)]属性的属性将成为位置。

  • 此外,制作[Parameter(...)]参数位置是没有意义的,因为他们按照定义非位置:您总是必须指定他们的名称(明确地),并允许将它们放在任何地方。

答案 1 :(得分:2)

我会合并两个开关,并根据您想要的组输入输入。

您可以使用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
)