PowerShell 无法根据位置参数的数量选取正确的 ParameterSet

PowerShell fails to pick correct ParameterSet based on number of positional parameters

提问人:JHR 提问时间:9/27/2023 最后编辑:JHR 更新时间:9/30/2023 访问量:84

问:

我正在尝试编写一个PowerShell函数,该函数使用ParameterSets公开其可用参数。我希望该函数具有类似于以下内容的参数集:Set-MyRef

Set-MyRef -Latest

Set-MyRef -LocalBranch

Set-MyRef [-Tag] <string>

Set-MyRef [-Env] <string> -LocalBranch

Set-MyRef [-Env] <string> -Latest

Set-MyRef [-Env] <string> [-Tag] <string>

也就是说,恰好是其中一个选项,或者可以给出一个选项,并将可选作为第一个位置参数。
重要的是,我希望被解析为 ,并被解析为 。
-Latest-LocalBranch-Tag-EnvSet-MyRef 'foo'-Tag 'foo'Set-MyRef 'foo' 'bar'-Env 'foo' -Tag 'bar'

我尝试使用ParameterSets实现它:

function Set-MyRef {
    [CmdletBinding()]
    param (
        [Parameter(Position = 0, Mandatory = $true, ParameterSetName = 'EnvTag')]
        [Parameter(Position = 0, Mandatory = $true, ParameterSetName = 'EnvLatest')]
        [Parameter(Position = 0, Mandatory = $true, ParameterSetName = 'EnvLocal')]
        [string]$Env,
        [Parameter(Position = 0, Mandatory = $true, ParameterSetName = 'Tag')]
        [Parameter(Position = 1, Mandatory = $true, ParameterSetName = 'EnvTag')]
        [string]$Tag,
        [Parameter(Mandatory = $true, ParameterSetName = 'Latest')]
        [Parameter(Mandatory = $true, ParameterSetName = 'EnvLatest')]
        [switch]$Latest,
        [Parameter(Mandatory = $true, ParameterSetName = 'Local')]
        [Parameter(Mandatory = $true, ParameterSetName = 'EnvLocal')]
        [switch]$LocalBranch
    )

    Write-Host "{ Env: $Env, Tag: $Tag, Latest: $Latest, LocalBranch: $LocalBranch }"
}

这给出了正确的解析语法:

$ Get-Command Set-MyRef -Syntax

Set-MyRef [-Env] <string> -LocalBranch [<CommonParameters>]

Set-MyRef [-Env] <string> -Latest [<CommonParameters>]

Set-MyRef [-Env] <string> [-Tag] <string> [<CommonParameters>]

Set-MyRef [-Tag] <string> [<CommonParameters>]

Set-MyRef -Latest [<CommonParameters>]

Set-MyRef -LocalBranch [<CommonParameters>]

但无法正确解析参数。当仅使用一个位置参数调用它时,它会将其用于 和 :$Env$Tag

$ Set-MyRef 'foo' # incorrect - expect Env = $null, Tag = 'foo'
{ Env: foo, Tag: foo, Latest: False, LocalBranch: False }
$ Set-MyRef -Tag 'foo' # correct
{ Env: , Tag: foo, Latest: False, LocalBranch: False }

如何更改我的 ParameterSet 规范,以便在仅给出一个位置参数时 PowerShell 可以正确选取 ParameterSet?Set-MyRef [Tag] <string>

PowerShell 参数集 powershell-7

评论

0赞 Santiago Squarzon 9/28/2023
你所期望的,在立场论证中根本不可能实现。

答:

6赞 mklement0 9/28/2023 #1

看起来您遇到了一个错误,其中单个位置参数被错误地绑定到 ,似乎是由于两者都具有参数属性属性,尽管这些属性属于不同的参数集-Tag-EnvPosition=0

  • 注意:虽然您的方案很不寻常,因为第一个位置参数应绑定到哪个参数取决于目标参数集,但它在逻辑上是明确的,并且事实上的行为没有意义 - 请参阅 GitHub 问题 #20405

解决方法

  • 最好先更改位置参数的顺序,先绑定 -Tag,然后再绑定 -Env
function Set-MyRef {
  [CmdletBinding(DefaultParameterSetName='Tag')]
  param (
      [Parameter(Position = 1, Mandatory, ParameterSetName = 'EnvTag')]
      [Parameter(Position = 0, Mandatory, ParameterSetName = 'EnvLatest')]
      [Parameter(Position = 0, Mandatory, ParameterSetName = 'EnvLocal')]
      [string]$Env,
      [Parameter(Position = 0, Mandatory, ParameterSetName = 'Tag')]
      [Parameter(Position = 0, Mandatory, ParameterSetName = 'EnvTag')]
      [string]$Tag,
      [Parameter(Mandatory, ParameterSetName = 'Latest')]
      [Parameter(Mandatory, ParameterSetName = 'EnvLatest')]
      [switch]$Latest,
      [Parameter(Mandatory, ParameterSetName = 'Local')]
      [Parameter(Mandatory, ParameterSetName = 'EnvLocal')]
      [switch]$LocalBranch
  )

  Write-Host "{ Env: $Env, Tag: $Tag, Latest: $Latest, LocalBranch: $LocalBranch }"
}
  • 否则,解决方法会变得很麻烦:
function Set-MyRef {
  [CmdletBinding(DefaultParameterSetName='Env')]
  param (
      [Parameter(Position = 0, Mandatory, ParameterSetName = 'EnvTag')]
      [Parameter(Position = 0, Mandatory, ParameterSetName = 'EnvLatest')]
      [Parameter(Position = 0, Mandatory, ParameterSetName = 'EnvLocal')]
      [string]$Env,
      [Parameter(Position = 0, Mandatory, ParameterSetName = 'Tag')]
      [Parameter(Position = 1, Mandatory = $false, ParameterSetName = 'EnvTag')]
      [string]$Tag,
      [Parameter(Mandatory, ParameterSetName = 'Latest')]
      [Parameter(Mandatory, ParameterSetName = 'EnvLatest')]
      [switch]$Latest,
      [Parameter(Mandatory, ParameterSetName = 'Local')]
      [Parameter(Mandatory, ParameterSetName = 'EnvLocal')]
      [switch]$LocalBranch
  )

  # Compensate for the broken parameter binding, by inferring
  # from the fact that the -Tag argument is the same as the -Env argument
  # that only *one* positional argument was passed and that it was meant to
  # bind to -Tag.
  if ($PSCmdlet.ParameterSetName -eq 'EnvTag' -and $Env -eq $Tag) {
    $Env = $null
  }
  Write-Host "{ Env: $Env, Tag: $Tag, Latest: $Latest, LocalBranch: $LocalBranch }"
}
  • 注意:

    • 上面假设没有合法的用例,其中 and 参数恰好具有相同的值。通过更多的努力,后一种情况也可以得到处理。-Env-Target

评论

0赞 JHR 9/28/2023
虽然这修复了单参数情况,但不幸的是,它看起来在双参数情况下交换了 / 参数:应该解析为 ,但被解析为 '-Env 'bar' -Tag 'foo'。-Tag-EnvSet-MyRef 'foo' 'bar'-Env 'foo' -Tag 'bar'
0赞 JHR 9/28/2023
我假设否定是未定义的行为,因为我在任何文档中都找不到它(并且在运行时会导致错误)。不过,这是一个巧妙的技巧!PositionGet-Command Set-MyRef -Syntax
0赞 mklement0 9/28/2023
好点,@JHR;我已经更新了答案,以注意到建议的解决方法不是其中之一。我会再考虑一下。
0赞 mklement0 9/28/2023
@JHR,请查看我的更新是否有帮助。
1赞 mklement0 9/30/2023
很高兴听到它,@JHR;别客气。我创建了 GitHub 问题 #20405 来讨论意外行为。