至少设置 PowerShell 所需的众多参数之一

问题描述 投票:0回答:3

在PowerShell中,我想编写一个函数,它接受不同的选项作为参数。如果它接收到多个参数就可以,但它必须接收至少一个参数。我想通过参数定义来强制执行它,而不是通过之后的代码。我可以使用以下代码让它工作:

function Set-Option {

    Param(
        [Parameter(Mandatory, ParameterSetName="AtLeastOption1")]
        [Parameter(Mandatory=$false, ParameterSetName="AtLeastOption2")]
        [Parameter(Mandatory=$false, ParameterSetName="AtLeastOption3")]
        $Option1,

        [Parameter(Mandatory=$false, ParameterSetName="AtLeastOption1")]
        [Parameter(Mandatory, ParameterSetName="AtLeastOption2")]
        [Parameter(Mandatory=$false, ParameterSetName="AtLeastOption3")]
        $Option2,

        [Parameter(Mandatory=$false, ParameterSetName="AtLeastOption1")]
        [Parameter(Mandatory=$false, ParameterSetName="AtLeastOption2")]
        [Parameter(Mandatory, ParameterSetName="AtLeastOption3")]
        $Option3
    )

    # Do stuff, but don't evaluate the plausibility of the given parameters here
}

但正如你所看到的,它的扩展性很差。对于每个附加选项,我必须向所有其他选项添加一行。这可以以更高效、更可维护的方式完成吗?

正如我已经说过的,我不想检查代码中的参数,例如。 G。通过评估

$PSBoundParameters
。出于自动文档的原因,我希望它发生在参数定义中。


如果您需要一个真实世界的示例,请查看

Set-DhcpServerv4OptionValue
,它接受许多不同的选项(
-DnsDomain
-DnsServer
-Router
,...),可以拥有所有选项,但没有任何意义。


注意:在提供了几个答案之后,我刚刚意识到,如果您提供多个选项,我的代码实际上工作。

powershell parameters optional-parameters powershell-5.1
3个回答
5
投票

以下不是一个很好的解决方案 - 根据您所说的 auto-doc 的含义,它可能不适合您 - 但它可以很好地扩展,因为您只需要 one 附加参数集:

function Set-Option {

  [CmdletBinding(DefaultParameterSetName='Fail')]
  Param(
      [Parameter(ParameterSetName='AtLeastOne')]
      $Option1,

      [Parameter(ParameterSetName='AtLeastOne')]
      $Option2,

      [Parameter(ParameterSetName='AtLeastOne')]
      $Option3,

      # Note: All that 'DontShow' does is to exclude the param. from tab completion.
      [Parameter(ParameterSetName='Fail', DontShow)] 
      ${-} = $(if ($PScmdlet.ParameterSetName -eq 'Fail') { throw "Please specify at least one option." })
  )

  # Do stuff, but without needing to evaluate the plausibility of
  # the given parameters here.

}
  • 所有实际参数都是可选,并且属于同一参数集,不是默认参数。

  • 虚拟参数

    ${-}
    是默认参数集中唯一的一个,其目的只是通过其default值抛出错误。

    • 由于默认值是always计算的,因此仅当选择默认参数集时才需要抛出条件(

      $PScmdlet.ParameterSetName -eq 'Fail'
      )。

    • 由于参数的名称不规则,您实际上无法向它传递显式值(这在这里是可取的,因为它纯粹是辅助的,不适合直接使用):您必须使用

      -- <value>
      ,但是
      -- 
      对参数绑定器具有特殊含义(停用后续参数的命名参数绑定)。

    • 不幸的是,属性

      DontShow
      (例如
      [Parameter(DontShow)]
      )仅隐藏了 tab-completion 中的参数,而不是 语法图中的参数。

因此,不幸的是,虚拟参数集及其参数出现在语法图中,因此

Set-Option -?
显示以下内容:

SYNTAX
    Set-Option [-- <Object>] [<CommonParameters>]

    Set-Option [-Option1 <Object>] [-Option2 <Object>] [-Option3 <Object>] [<CommonParameters>]

请注意,语法图缺少您所需逻辑的符号。


2
投票

编辑:正如OP指出的,这里提出的解决方案在传递多个参数时不起作用:

Set-Option -Option1 foo -Option2 42

Parameter set cannot be resolved using the specified named parameters. One or more    
parameters issued cannot be used together or an insufficient number of parameters were
provided.

我会将其作为使用

DynamicParam
的示例。


这是一个使用

DynamicParam
自动生成与您手动创建的相同参数集的解决方案。尽管不是“无代码”解决方案,但它仍然显示预期的语法图(当像
Set-Option -?
那样调用时),因为 PowerShell 从
DynamicParam
块获取所有必要的信息。

首先我们定义一个可重用的辅助函数,以便能够编写 DRY

DynamicParam
块:

Function Add-ParamGroupAtLeastOne {
    <#
    .SYNOPSIS
        Define a group of parameters from which at least one must be passed.
    #>
    Param(
        [Parameter(Mandatory)] [Management.Automation.RuntimeDefinedParameterDictionary] $Params,
        [Parameter(Mandatory)] [Collections.Specialized.IOrderedDictionary] $ParamDefinitions
    )

    foreach( $paramDef in $ParamDefinitions.GetEnumerator() ) {

        $attributes = [Collections.ObjectModel.Collection[Attribute]]::new()

        # Generate parameter sets for one parameter
        foreach( $groupItem in $ParamDefinitions.Keys ) {
            $attr = [Management.Automation.ParameterAttribute]@{
                Mandatory = $paramDef.Key -eq $groupItem
                ParameterSetName = "AtLeastOne$groupItem"
            }
            if( $paramDef.Value.HelpMessage ) {
                $attr.HelpMessage = $paramDef.Value.HelpMessage
            }
            
            $attributes.Add( $attr )
        }
    
        # Add one parameter
        $Params.Add( $paramDef.Key, [Management.Automation.RuntimeDefinedParameter]::new( $paramDef.Key, $paramDef.Value.Type, $attributes ))         
    }
}

Set-Option
函数现在可以这样写:

Function Set-Option {
    [CmdletBinding()]
    Param()  # Still required

    DynamicParam {
        $parameters = [Management.Automation.RuntimeDefinedParameterDictionary]::new()

        Add-ParamGroupAtLeastOne -Params $parameters -ParamDefinitions ([ordered] @{ 
            Option1 = @{ Type = 'string'; HelpMessage = 'the 1st option' }
            Option2 = @{ Type = 'int';    HelpMessage = 'the 2nd option' }
            Option3 = @{ Type = 'bool';   HelpMessage = 'the 3rd option' }
        })

        $parameters
    }    

    process {
        # Do stuff
    }
}

Set-Option -?
按预期输出此语法图:

SYNTAX
    Set-Option -Option1 <string> [-Option2 <int>] [-Option3 <bool>] [<CommonParameters>]

    Set-Option -Option2 <int> [-Option1 <string>] [-Option3 <bool>] [<CommonParameters>]

    Set-Option -Option3 <bool> [-Option1 <string>] [-Option2 <int>] [<CommonParameters>]

如果您想添加更多参数属性,请查看

ParameterAttribute
类并在函数
Add-ParamGroupAtLeastOne
中添加所需的属性,就像我为
HelpMessage
所做的示例一样。


0
投票

如果参数都是开关(即,将它们指定为

-Option1
而不是
-Option1 SomeValue
),请在实际代码的开头包含一个测试,检查它们是否全部为 false,如果是,则拒绝调用。如果它们是值参数(即
-Option1 SomeValue
),则必须针对
$null
测试每个参数,如果它们都是
$null
,则拒绝调用。

function Set-Option {
   param (
      [switch]$Option1,
      [switch]$Option2,
      ...
   )

   if (!($Option1 -or $Option2 -or ...)) {
      # reject the invocation and abort
   }
   ...
}
© www.soinside.com 2019 - 2024. All rights reserved.