如何支持多个互斥的参数?

lit*_*lit 2 parameters powershell parameter-sets powershell-7.3

我必须编写一个脚本来获取 Thing 的实例。每个事物都包含一个事件时间戳。我需要允许用户指定时间戳范围。

  • 有四 (4) 个时间指定参数
  • 没有一个是强制性的
  • $Since 和 $StartTimestamp 是互斥的
  • $Until 和 $EndTimestamp 是互斥的
  • 该脚本会将 $Since 和 $Until 字符串转换为适当的 [datetime]

如何使用 ParameterSet 禁用 $Since 和 $StartTimestamp 的使用并禁用 $Until 和 $EndTimestamp 的使用?

关于使用多个参数集的帖子似乎随着参数数量呈指数增长。真的是这样吗?

有关于使用 DynamicParam 的帖子。我还没有看到 DynamicParam 适合于此。是吗?

[CmdletBinding()]
param (
    [Parameter(Mandatory=$true)]
    [string] $ThingName

    ,[ValidateSet('Today', 'Yesterday', 'LastWeek')]
    [string] $Since

    ,[datetime] $StartTimestamp

    ,[ValidateSet('Today', 'Now', 'Yesterday', 'LastWeek')]
    [string] $Until

    ,[datetime] $EndTimestamp
)
Run Code Online (Sandbox Code Playgroud)

mkl*_*nt0 5

截至撰写本文时,互斥参数确实很难使用参数集来实现(PowerShell v7.3.4):

  • GitHub 问题 #5175是一个长期存在的功能请求,旨在使定义互斥参数变得更容易,但它有
  • GitHub 问题 #12818是一个更新且更全面的功能,还涵盖参数集的其他方面。

具有当前功能的解决方案是可能的,但很麻烦:

注意:我假设您只想允许以下组合(这补充了您想要在问题中防止的内容的描述),并且这种积极的表述可以通过参数集来表达:

  • -ThingName仅,或与以下任何一项组合:
  • -Since仅仅、-Until仅仅
  • -StartTimestamp仅仅、-EndTimeStamp仅仅
  • -Since结合-EndTimeStamp
  • -Until结合-StartTimeStamp
[CmdletBinding(DefaultParameterSetName='ThingAlone')]
param (
    [Parameter(Mandatory, Position=0)]
    [string] $ThingName
    ,    
    [Parameter(Mandatory, ParameterSetName='SinceAlone')]
    [Parameter(Mandatory, ParameterSetName='StartSinceEndUntil')]
    [Parameter(Mandatory, ParameterSetName='StartSinceEndTimestamp')]
    [ValidateSet('Today', 'Yesterday', 'LastWeek')]
    [string] $Since
    ,
    [Parameter(Mandatory, ParameterSetName='StartTimestampAlone')]
    [Parameter(Mandatory, ParameterSetName='StartTimestampEndUntil')]
    [Parameter(Mandatory, ParameterSetName='StartTimestampEndTimestamp')]
    [datetime] $StartTimestamp
    ,
    [Parameter(Mandatory, ParameterSetName='UntilAlone')]
    [Parameter(Mandatory, ParameterSetName='StartSinceEndUntil')]
    [Parameter(Mandatory, ParameterSetName='StartTimestampEndUntil')]
    [ValidateSet('Today', 'Now', 'Yesterday', 'LastWeek')]
    [string] $Until
    ,
    [Parameter(Mandatory, ParameterSetName='EndTimeStampAlone')]
    [Parameter(Mandatory, ParameterSetName='StartSinceEndTimestamp')]
    [Parameter(Mandatory, ParameterSetName='StartTimestampEndTimestamp')]
    [datetime] $EndTimestamp
)

$PSCmdlet.ParameterSetName
Run Code Online (Sandbox Code Playgroud)

生成的语法图(使用 调用脚本-?):

YourScript.ps1 [-ThingName] <string> [<CommonParameters>]
YourScript.ps1 [-ThingName] <string> -Since <string> -EndTimestamp <datetime> [<CommonParameters>]
YourScript.ps1 [-ThingName] <string> -Since <string> -Until <string> [<CommonParameters>]
YourScript.ps1 [-ThingName] <string> -Since <string> [<CommonParameters>]
YourScript.ps1 [-ThingName] <string> -StartTimestamp <datetime> -EndTimestamp <datetime> [<CommonParameters>]
YourScript.ps1 [-ThingName] <string> -StartTimestamp <datetime> -Until <string> [<CommonParameters>]
YourScript.ps1 [-ThingName] <string> -StartTimestamp <datetime> [<CommonParameters>]
YourScript.ps1 [-ThingName] <string> -Until <string> [<CommonParameters>]
YourScript.ps1 [-ThingName] <string> -EndTimestamp <datetime> [<CommonParameters>]
Run Code Online (Sandbox Code Playgroud)

退一步说

  • 正如zett42指出的,如果您分别为开始和结束时间戳提供单个多态参数,则可以绕过互斥的需要。

  • 为此,将这些参数声明为[object](以便它们可以接受任何类型的值),并且:

    • 使用[ValidateScript()]属性来确保用户传递的值可以被解析为实例[datetime]或者是预定义的符号名称之一,例如Today.

    • 为了还支持制表符完成,请使用[ArgumentCompleter()]完成符号名称的属性。

    • 注意:符号名称数组在以下两个属性中重复:

      • 在无法避免的独立脚本中,但在函数中(例如作为模块的一部分),您只能定义数组一次。
[CmdletBinding()]
param (
    [Parameter(Mandatory, Position=0)]
    [string] $ThingName
    ,
    [ArgumentCompleter({
      param($cmd, $param, $wordToComplete)
      'Today', 'Yesterday', 'LastWeek' -like "$wordToComplete*"
    })]
    [ValidateScript({
      if ($_ -notin 'Today', 'Yesterday', 'LastWeek' -and $null -eq ($_ -as [datetime])) {
        throw "Invalid -Since argument."
      }
      $true
    })]
    [object] $Since
    ,
    [ArgumentCompleter({
      param($cmd, $param, $wordToComplete)
      'Today', 'Now', 'Yesterday', 'LastWeek' -like "$wordToComplete*"
    })]
    [ValidateScript({
      if ($_ -notin 'Today', 'Now', 'Yesterday', 'LastWeek' -and $null -eq ($_ -as [datetime])) {
        throw "Invalid -Until argument."
      }
      $true
    })]
    [object] $Until
)

# Translate the -Since and -Until arguments into [datetime] instances.
$i = 0
$sinceTimestamp, $untilTimestamp = 
    $Since, $Until | ForEach-Object {
      switch ($_) {
        $null { if ($i -eq 0) { Get-Date -Date 0 } else { Get-Date }; break }
        Now { Get-Date; break }
        Today { (Get-Date).Date; break }
        Yesterday  { (Get-Date).Date.AddDays(-1); break }
        LastWeek  { (Get-Date).Date.AddDays(-7); break }
        Default { $_ -as [datetime] }
      }
      ++$i
    }
if ($untilTimestamp -lt $sinceTimestamp) { Throw "The -Since argument must predate the -Until argument." }

# Diagnostic output.
[pscustomobject] @{
  Since = $sinceTimestamp
  Until = $untilTimestamp
}
Run Code Online (Sandbox Code Playgroud)