【问题标题】:Does powershell have a way to handle calling a command with multiple parameter sets?powershell 是否有办法处理调用具有多个参数集的命令?
【发布时间】:2018-03-02 20:58:38
【问题描述】:

要清楚,我并不是说我的命令需要接受多个参数集,它需要调用具有多个参数集的函数。

我的脚本

[CmdletBinding()]
Param(
    [Parameter(Mandatory=$true, ParameterSetName='NamedCreds')]
    [PSCredential] $Credential,

    [Parameter(Mandatory=$true, ParameterSetName='NamedCreds')]
    [Parameter(Mandatory=$true, ParameterSetName='DefaultCreds')]
    [string] $Url,

    [Parameter(Mandatory=$true, ParameterSetName='NamedCreds')]
    [Parameter(Mandatory=$true, ParameterSetName='DefaultCreds')]
    [string] $Path,

    [Parameter(Mandatory=$true, ParameterSetName='DefaultCreds')]
    [switch] $UseDefaultCredentials
)


#repeating this if block gets tedious
if ($PSCmdlet.ParameterSetName -ieq "NamedCreds")
{
    $result = Invoke-RestMethod -Uri $Url -OutFile $Path -Credential $Credential
}
else
{
    $result = Invoke-RestMethod -Uri $Url -OutFile $Path -UseDefaultCredentials
}

#do something with result

我想避免使用“if 参数集名称,否则”条件句,这会导致脚本乱扔垃圾。我已经阅读了有关“飞溅”的内容,但它的示例将其解释为避免向右滚动以获取长参数列表的一种方式,并且不涵盖参数集或可选参数。有没有办法调用其他命令并正确处理可选参数和参数集?

【问题讨论】:

    标签: powershell


    【解决方案1】:

    喷溅是(某种程度上)这个问题的答案。

    您需要您的条件来确定要传递的参数。您可以使用它们构建哈希表,然后传递一次。

    对于您要执行的操作,将-Url 参数重命名为-Uri 并将$Path 重命名为$OutFile 可能更有意义(如果您喜欢其他名称,您可以随时添加别名更好),然后直接喷$PSBoundParameters

    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$true, ParameterSetName='NamedCreds')]
        [PSCredential] $Credential,
    
        [Parameter(Mandatory=$true, ParameterSetName='NamedCreds')]
        [Parameter(Mandatory=$true, ParameterSetName='DefaultCreds')]
        [Alias('Url')]
        [string] $Uri,
    
        [Parameter(Mandatory=$true, ParameterSetName='NamedCreds')]
        [Parameter(Mandatory=$true, ParameterSetName='DefaultCreds')]
        [Alias('Path')]
        [string] $OutFile,
    
        [Parameter(Mandatory=$true, ParameterSetName='DefaultCreds')]
        [switch] $UseDefaultCredentials
    )
    
    
        $result = Invoke-RestMethod @PSBoundParameters
    }
    

    将在此处处理您的评论:

    这是一个缩略的例子,通常有更多的参数 除此之外,还有在脚本运行时派生的变量 执行 get 传递给被调用的命令。就是它 要改变这个例子吗?

    是的,不幸的是,像这样直接使用$PSBoundParameters 效果很好,直到它不起作用,这通常比您想象的要快(可选参数、某些自动参数等存在问题)。

    真正的意思是:当你有条件时,你可能无法避免条件。

    当它开始变得如此复杂时,忽略参数集,只测试每个参数,除了那些强制或具有默认值的参数(在所有集合中)。

    我刚刚注意到你在为某些系列命名;你不需要这样做。如果[Parameter()] 属性的内容在每个集合中都相同,那么您可以只使用单个[Parameter()] 属性而不用命名集合;我将在下面更改您的示例以反映这一点。

    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$true, ParameterSetName='NamedCreds')]
        [PSCredential] $Credential,
    
        [Parameter(Mandatory=$true)]
        [Alias('Url')]
        [string] $Uri,
    
        [Parameter(Mandatory=$true)]
        [string] $Path,
    
        [Parameter(Mandatory=$true, ParameterSetName='DefaultCreds')]
        [switch] $UseDefaultCredentials
    )
    
        $params = @{
            Uri = $Uri
            Path = $Path
        }
    
        if ($Credential) {
            $params.Credential = $Credential
        }
    
        if ($PSBoundParameters.ContainsKey('UseDefaultCredentials')) {
            $params.UseDefaultCredentials = $UseDefaultCredentials
        }
    
        $result = Invoke-RestMethod @params
    
    }
    

    这是我通常使用的模式。构建一个哈希表,然后将其分解一次。

    有一些微妙之处需要注意,所以我将进一步分解。

    $params = @{
        Uri = $Uri
        OutFile = $Path
    }
    

    在这里创建[hashtable]。同时,您可以填充将始终存在的任何成员。 UriPath 是每个参数集中的强制参数,因此基本上代码无法到达此处,除非它们具有值(或更准确地说;在任何情况下,它们都不能从传递给 @ 的参数中省略987654335@)。

    如果您没有任何这些参数,则将其设为空 $params = @{}

    请注意,对于 $Path,我们将键 OutFile 与将要调用的 cmdlet 中的名称匹配(因此您可以通过这种方式拥有自己的参数名称)。

    if ($Credential) {
        $params.Credential = $Credential
    }
    

    很简单;测试参数的真实性。如果那里有东西,请将其添加到$params

    if ($PSBoundParameters.ContainsKey('UseDefaultCredentials')) {
        $params.UseDefaultCredentials = $UseDefaultCredentials
    }
    

    这是其中一种特殊情况。如果我们改用基本条件检查:

    if ($UseDefaultCredentials) {
        $params.UseDefaultCredentials = $UseDefaultCredentials
    }
    

    那么我们有一个小问题,因为您可以使用如下显式值调用开关参数:Invoke-RestMethod -UseDefaultCredentials:$false。如果你使用简单的条件,你不会通过它,但是通过检查它是否被绑定,你将检查参数是否被指定,不管它的值是什么。

    您不必总是知道;你可能不想支持用 false 显式调用 switch 参数,在这种情况下继续做一个简单的检查。

    另请注意,当您指定参数为Mandatory 时,您也隐含地不允许某些虚假值。 Mandatory [String] 不允许空字符串或 $null。数组参数不允许空数组。您必须使用其他属性显式启用它们,例如[AllowEmptyString()]。如果你这样做,简单的检查也不适用于那些,因为你没有传递真正的价值;这是另一个检查$PSBoundParameters 很有帮助的例子。

    可以对 switch 参数做的另一件事就是始终将其转换为 [bool] 并将其放入哈希表中:

    $params.UseDefaultCredentials = $UseDefaultCredentials -as [bool]
    # or, during initialization
    $params = @{
        Uri = $Uri
        UseDefaultCredentials = $UseDefaultCredentials -as [bool]
    }
    

    这只能通过不属于参数集的开关来完成,否则你会将它提供给被调用的命令,即使它不在你的函数上,这可能会导致无效的参数集选择。


    你可以做的其他事情:

    使用switch ($PSCmdlet.ParameterSetName) 而不是大量的ifs,如果在集合上交替更重要(例如,如果您的某些计算值比任何一个特定参数更依赖集合)。

    从绑定参数入手并修改:

    $params = $PSBoundParameters.Clone()
    
    $params.OutFile = $params.Path
    $params.Remove('Path')
    
    $params.NewValue = Invoke-MyAlgorithm
    

    显然没有万能的解决方案;无论您采用哪种方法,都取决于您正在做什么以及要求是什么。

    一个共同点是:构建一个参数哈希表,然后将其分解一次*

    * 例外情况适用:-p

    【讨论】:

    • 这是一个简短的例子,通常有比这更多的参数,以及在脚本执行时派生的变量,这些变量被传递到被调用的命令中。这会改变这个例子吗? (RE:Url/Uri - 变老很糟糕。如果可以,请不要这样做。区分 i 和 l,以及某些字体的逗号和句点很难)
    • O_O 老兄,谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-12-08
    • 1970-01-01
    • 2017-07-24
    • 2017-03-24
    • 2018-03-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多