如何更改参数的抛出消息?

How do you change a parameter's throw message?

问题: 希望更改显示的错误消息以说明他们需要将“-passed、-warning 或-failed”包含在 cmdlet 参数中,或者不关心这三个是否丢失并编写消息(假设它在那里) .

说明: 创建了一个接受三个参数的函数:消息、第二条消息和消息状态(通过, 失败,警告)。我收到默认错误消息“无法使用指定的命名解析参数 参数。”无论您是否通过以下方式传递消息,都会发生这种情况:

PS C:\> Write-Message;
Write-Message : Parameter set cannot be resolved using the specified named parameters.
...
//or
PS C:\> Write-Message -Message "Hello World";

但是,如果您输入 'status' 参数,它将毫无问题地工作:

PS C:\> Write-Message -Message "Hello World" -Passed;
Hello World 

('Hello World' 应该是这里的绿色,但 Stack Overflow 还没有那个特性)

有问题的函数:

Function Write-Message {
    param(
        [string]$Message,
        [string]$MessageTwo,
        [Parameter(Mandatory=$false, ValueFromPipelineByPropertyName = $true, ParameterSetName ="Passed")][switch]$Passed,
        [Parameter(Mandatory=$false, ValueFromPipelineByPropertyName = $true, ParameterSetName ="Warning")][switch]$Warning,
        [Parameter(Mandatory=$false, ValueFromPipelineByPropertyName = $true, ParameterSetName ="Failed")][switch]$Failed
    );
    $NewLineNeeded = [string]::IsNullOrEmpty($MessageTwo);

    if ($Passed){
        $color = 'green';
    }
    if($Warning){
        $color = 'yellow';
    }
    if($Failed){
        $color = 'red';
    }

    if($Passed -or $Warning -or $Failed){
        if(!$NewLineNeeded){
            Write-Host $MessageOne -NoNewline;
            Write-Host $MessageTwo -ForegroundColor $color;
        } else {
            Write-Host $Message -ForegroundColor $color;
        }  
    }
}

添加一个 "default" 参数集来解决这个问题。您实际上不必在代码中使用参数集,因此即使使用 "Default" 之类的东西也可以在不对代码进行额外更改的情况下使用。在编写更复杂的 cmdlet 时,我自己 运行 在其中使用参数集作为互斥参数,但有些参数与参数集无关。

Function Write-Message {
  [CmdletBinding(DefaultParameterSetName="Default")]
  Param(
...

当您只使用没有参数集的参数时,Powershell 似乎对正在使用哪个参数集感到困惑,并且您还在 cmdlet 中定义了参数集。

您只需满足以下所有条件即可:

  • 已明确定义两个或更多参数集
  • 尚未将现有参数集之一绑定为默认值
  • 具有(并调用您的例程)未绑定到任一集合的参数

中所述,连同发生的一些幕后信息,这可能是某种错误,此解决方案是解决方法。

用更多背景信息补充:

未通过其 [Parameter()] 属性标记为至少属于一个参数集的参数:

  • 被隐含地视为 所有 定义参数集的一部分。

  • 分配了自动 __AllParameterSets 参数集。

您可以使用 Write-Message 函数验证如下:

(Get-Command Write-Message).Parameters.GetEnumerator() | 
  Select-Object @{ n='ParameterName'; e = { $_.Key } },
    @{ n='ParameterSets'; e = { $_.Value.ParameterSets.GetEnumerator().ForEach('Key') } }

以上结果(常用参数省略):

ParameterName       ParameterSets
-------------       -------------
Message             __AllParameterSets
MessageTwo          __AllParameterSets
Passed              Passed
Warning             Warning
Failed              Failed
# ...

重要:一旦您将参数标记为属于参数集,它属于该参数集,如果它需要属于其他人,你需要多个,单独的[Parameter(ParameterSetName = '...')]属性——每个感兴趣的参数集一个。

当使用一组给定的参数值调用给定的函数或脚本时,PowerShell 会尝试明确地推断要应用的参数集并报告 selected 参数集 $PSCmdlet.ParameterSetName.

如果无法明确推断单个参数集,则会发生语句终止错误;也就是说,函数的调用根本失败了(但默认情况下脚本的执行会继续)。 发生这种情况的原因有两个:

  • 传递了互斥参数:指定了属于不同参数集的参数(没有至少一个共同的关联参数集的参数)。

  • 传递了一个不明确的参数组合:不清楚select 设置的参数是什么,因为可以应用多个参数。

两种情况下的错误信息是一样的:

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.

顺便说一句:如果您仅指定参数名称的 prefix(PowerShell 通常支持),并且该前缀不明确,即匹配 多个参数;例如,-Out 是不明确的,因为它同时匹配 -OutVariable-OutBuffer:

Parameter cannot be processed because the parameter name 'Out' is ambiguous. Possible matches include: -OutVariable -OutBuffer.

如果指定参数或仅指定未明确标记为属于参数集的参数and 您还没有定义 default 参数集,PowerShell selects __AllParameterSets 集并反映在 $PSCmdlet.ParameterSetName.
然而,令人惊讶的是,只有 one 明确定义的参数集才有效。

有两个或多个显式参数集 - 如您的情况 - PowerShell 认为这种情况不明确并报告错误。

这种令人惊讶的行为在 this GitHub issue 中进行了讨论。

解决方法,如 Bender 的回答所示,是定义一个默认参数集 通过放置在 param(...) 块上方的 [CmdletBinding()] 属性;例如:[CmdletBinding(DefaultParameterSetName='Default')]

这个参数集是隐式创建的,名称可以自由选择;没有任何实际参数必须被标记为属于它。

现在,当您不指定任何参数或仅指定那些没有显式参数集标记的参数时,调用会成功,并且 $PSCmdlet.ParameterSetName 反映默认参数集的名称。