【问题标题】:How do I force at least one parameter from a set be specified?如何强制指定一组参数中的至少一个参数?
【发布时间】:2015-11-30 14:12:40
【问题描述】:

我有一个 PowerShell 函数,它接受一个字符串和三个开关。所以调用会是这样的:

My-Function "some string" -Switch1 -Switch2 -Switch3

但是,我想让这个函数只在提供字符串和至少一个开关的情况下运行。所以:

My-Function "some string" -Switch1 -Switch3  # Valid
My-Function "some string" -Switch2           # Valid
My-Function "some string"                    # Invalid

我知道我可以通过使用$MyInvocation 对象检查开关是否已通过,但有没有办法使用ParameterParameterSet 属性来做到这一点?

简而言之,这就是我想要做的:

  • 每次调用函数时都必须提供字符串。
  • 每次调用该函数时必须至少提供一个开关。

【问题讨论】:

    标签: powershell


    【解决方案1】:

    绝对有可能要求使用 ParameterSets 指定至少 n 个参数中的一个:

    function My-Function() {
        Param(
            [Parameter(Mandatory, Position=0)]
            [string]$Text,
    
            [Parameter(Mandatory, ParameterSetName='o1')]
            #not member of the following ParameterSets
            [Switch][bool]$Switch1,
    
            [Parameter(ParameterSetName='o1')] #non-mandatory member of previous ParameterSets
            [Parameter(Mandatory, ParameterSetName='o2')]
            #not member of the following ParameterSets
            [Switch][bool]$Switch2,
    
            [Parameter(ParameterSetName='o1')] #non-mandatory member of previous ParameterSets
            [Parameter(ParameterSetName='o2')] #non-mandatory member of previous ParameterSets
            [Parameter(Mandatory, ParameterSetName='o3')]
            [Switch][bool]$Switch3
        )
        $PSBoundParameters
        echo "`n"
    }
    
    #tests
    echo "These should work:"
    My-Function "some string" -Switch1 -Switch2 -Switch3
    My-Function "some string" -Switch1 -Switch2
    My-Function "some string" -Switch1          -Switch3
    My-Function "some string" -Switch1
    My-Function "some string"          -Switch2 -Switch3
    My-Function "some string"          -Switch2 
    My-Function "some string"                   -Switch3
    echo "This should not work:"
    My-Function "some string" 
    
    echo "However, this does work:"
    My-Function "some string" -Switch1:$false
    

    但是,参数集仅强制在调用命令时使用其中一个开关。

    这不是强制的,其中一个开关实际上设置为 true。 如果这是您需要的,那么在函数中进行实际测试,按照Ansgar Wiechers'answer 中的建议(稍作修改):

    if (-not ($Switch1.IsPresent -or $Switch2.IsPresent -or $Switch3.IsPresent)) {
        #throw 'Missing switch' #not good, error message does not show where My-Function was called...
        #Write-Error 'Missing switch' -ErrorAction Stop # better, but still stops the whole script, not just the function
        $PSCmdlet.ThrowTerminatingError([System.Management.Automation.ErrorRecord]::new(
            [System.Management.Automation.ParameterBindingException]'At least one of Switch1, Switch2, Switch3 must be true.',
            'MissingRequiredSwitch',
            [System.Management.Automation.ErrorCategory]::InvalidArgument,
            $null
        )) # statement-terminating error, same as generated when ParameterSet can not be resolved... 
    }
    

    关于语句终止错误见Difference between throw and $pscmdlet.ThrowTerminatingerror()?

    但是,我仍然建议使用参数集(除了显式测试)。因为如上所述使用 ParameterSets 会使 PSReadLine 显示在自动完成信息中,所以至少需要一个参数。示例输出,使用 ctrl+space 时:

    【讨论】:

      【解决方案2】:

      我认为你不能用参数集和强制参数来做到这一点。如果你用这样的交替强制参数定义了 3 个不同的参数集:

      Param(
        [Parameter(Mandatory=$true)]
        [string]$Text,
      
        [Parameter(Mandatory=$true, ParameterSetName='o1')]
        [Parameter(Mandatory=$false, ParameterSetName='o2')]
        [Parameter(Mandatory=$false, ParameterSetName='o3')]
        [Switch][bool]$Switch1,
      
        [Parameter(Mandatory=$false, ParameterSetName='o1')]
        [Parameter(Mandatory=$true, ParameterSetName='o2')]
        [Parameter(Mandatory=$false, ParameterSetName='o3')]
        [Switch][bool]$Switch2,
      
        [Parameter(Mandatory=$false, ParameterSetName='o1')]
        [Parameter(Mandatory=$false, ParameterSetName='o2')]
        [Parameter(Mandatory=$true, ParameterSetName='o3')]
        [Switch][bool]$Switch3
      )
      

      如果您只使用其中一个开关,它就可以工作:

      My-Function "foo" -Switch2
      

      但如果您使用多个开关会失败:

      PS C:\> .\test.ps1 "foo" -Switch1 -Switch2
      C:\test.ps1 : Parameter set cannot be resolved using the specified named
      parameters.
      At line:1 char:1
      + .\test.ps1 "foo" -Switch1 -Switch2
      + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          + CategoryInfo          : InvalidArgument: (:) [test.ps1], ParameterBindingException
          + FullyQualifiedErrorId : AmbiguousParameterSet,test.ps1
      

      我会改用经过设置验证的强制参数:

      Param(
        [Parameter(Mandatory=$true)]
        [string]$Text,
      
        [Parameter(Mandatory=$true)]
        [ValidateSet('Switch1', 'Switch2', 'Switch3', ignorecase=$true)]
        [string[]]$Options
      )
      

      这将允许您像这样调用函数:

      My-Function "foo" -Options Switch1
      My-Function "foo" -Options Switch2,Switch3
      

      或者您可以将所有三个开关设为可选并在函数内验证它们:

      Param(
        [Parameter(Mandatory=$true)]
        [string]$Text,
      
        [Parameter(Mandatory=$false)]
        [Switch][bool]$Switch1,
      
        [Parameter(Mandatory=$false)]
        [Switch][bool]$Switch2,
      
        [Parameter(Mandatory=$false)]
        [Switch][bool]$Switch3
      )
      
      if (-not ($Switch1.IsPresent -or $Switch2.IsPresent -or $Switch3.IsPresent)) {
        throw 'Missing switch'
      }
      

      Dynamic parameters 可能是另一种选择,但我不能肯定地说。

      【讨论】:

      • 我没想过使用ValidateSet。这实际上可能对这个功能更有效。谢谢你。
      • 使用 ParameterSets 实际上很有用 - 请参阅我的answer
      【解决方案3】:

      是的,您在这样定义时使用 parameter mandatory

       Param(
         [Parameter(Mandatory=$true)]
         [string]$myParameter
      ) 
      

      【讨论】:

      • OP 希望强制至少存在一个开关-Switch1-Switch2-Switch3。强制参数强制每次调用函数时都存在这个确切的开关。
      • 除此之外,每次只有字符串是强制性的。更新 OP 以反映这一点。
      猜你喜欢
      • 2016-12-09
      • 1970-01-01
      • 2016-05-10
      • 2021-02-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多