powershell有办法处理调用多个参数集的命令吗?

时间:2018-03-02 20:58:39

标签: powershell

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

我的剧本

[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参数集名称,否则"可能最终乱丢脚本的条件。我已经阅读过关于" splatting"但它的例子解释了它是一种避免向右滚动到长参数列表的方法,并且不包括参数集或可选参数。有没有办法调用其他命令并正确处理可选的参数和参数集?

1 个答案:

答案 0 :(得分:3)

Splatting是(有点)答案。

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

对于您要执行的操作,将-Url参数重命名为-Uri,将$Path重命名为$OutFile可能更有意义(您可以随时添加如果您更喜欢其他名称,请使用别名),然后直接splat $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
}

在此处发表您的评论:

  

这是一个缩写示例,通常有更多参数   比那个,以及脚本派生的变量   执行该传递给被调用的命令。就是它   要改变这个例子吗?

是的,不幸的是,直接使用$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是每个参数集中的强制参数,所以基本上代码不能到达,除非它们具有值(或者更准确;在任何条件下,它们都不能从参数中省略你转到Invoke-RestMethod)。

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

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

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

很容易;测试真实性的参数。如果有什么,请将其添加到$params

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

这是特殊情况之一。如果我们使用基本的条件检查:

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

然后我们遇到了一个小问题,因为您可以使用如下显式值调用switch参数: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)代替大量if s,如果在集合上交替更重要(例如,如果您的某些计算值比任何一个特定参数更依赖于集合)。

从绑定参数开始并修改它们:

$params = $PSBoundParameters.Clone()

$params.OutFile = $params.Path
$params.Remove('Path')

$params.NewValue = Invoke-MyAlgorithm

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

一个共同点是:构建一个params哈希表,然后将其展开一次 *

*例外情况适用:-p