How do you change a parameter's throw message?

蓝咒 提交于 2020-02-02 11:07:31

问题


Question: Looking to either change the error message displayed to say they need the '-passed, -warning, or -failed' to be included with the cmdlet parameters or to not care if the three are missing and to write the message (assuming its there).

Explanation: Created a function that takes in three arguments: a message, 2nd message, and state of message (pass, fail, warn). I get a default error message of "Parameter cannot be resolved using the specified named parameters." This happens regardless of if you're passing a message through:

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

But if you were to enter in the 'status' parameter it would work without any issues:

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

('Hello World' is supposed to be the color Green here, but Stack Overflow doesn't have that feature yet)

Function in question:

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;
        }  
    }
}

回答1:


Add a "default" parameter set to resolve this. You don't actually have to use the parameter set in your code, so even using something like "Default" would work without additional changes to your code. I've run into this myself when writing more complex cmdlets where I use parameter sets for mutually exclusive parameters, but some parameters are parameter-set agnostic.

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

It seems that Powershell gets confused about which parameter set is in use when you use only parameters without a parameter set, and you also have defined parameter sets in your cmdlet.

You only need to do this all of the following conditions are true:

  • Have explicitly defined two or more parameter sets
  • Have not already bound one of your existing parameter sets as the default
  • Have (and invoke your routine with) arguments that aren't bound to either set

As explained in mklement0's answer, along with some under-the-hood information of what happens, this is possibly a bug of sorts and this solution is the workaround.




回答2:


To complement Bender the Greatest' effective solution and helpful explanation with more background information:

Parameters that are not tagged as belonging to at least one parameter set, via their [Parameter()] attributes:

  • are implicitly considered part of all defined parameter sets.

  • are assigned the automatic __AllParameterSets parameter set.

You can verify this as follows with your Write-Message function:

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

The above yields (common parameters omitted):

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

Important: Once you tag a parameter as belonging to a parameter set, it belongs only to this parameter set, and if it also needs to belong to others, you need multiple, separate [Parameter(ParameterSetName = '...')] attributes - one for each parameter set of interest.

When a given function or script is invoked with a given set of parameter values, PowerShell tries to unambiguously infer what parameter set to apply and reports the selected parameter set in $PSCmdlet.ParameterSetName.

If a single parameter set cannot be unambiguously inferred, a statement-terminating error occurs; that is, invocation of the function fundamentally fails (but execution of the script continues by default). This can happen for one of two reasons:

  • Mutually exclusive parameters were passed: parameters belonging to different parameter sets were specified (parameters who do not have at least one associated parameters set in common).

  • An ambiguous combination of parameters was passed: it is not clear what parameter set to select, because multiple ones could apply.

The error message is the same in both cases:

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.

As an aside: An additional error may occur if you specified only a parameter name's prefix (which PowerShell generally supports), and that prefix is ambiguous, i.e. it matches multiple parameters; e.g., -Out is ambiguous, because it matches both -OutVariable and -OutBuffer:

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

If you specify no parameters or only specify parameters that aren't explicitly marked as belonging to a parameter set and you haven't defined a default parameter set, PowerShell selects the __AllParameterSets set and reflects that in $PSCmdlet.ParameterSetName.
Surprisingly, however, that only works if there's only one explicitly defined parameter set.

With two or more explicit parameter sets - as in your case - PowerShell considers the this case ambiguous and reports an error.

This surprising behavior is discussed in this GitHub issue.

The workaround, as shown in Bender's answer, is to define a default parameter set via the [CmdletBinding()] attribute placed above the param(...) block; e.g.: [CmdletBinding(DefaultParameterSetName='Default')]

This parameter set is implicitly created and its name can be freely chosen; no actual parameters haven to be tagged as belonging to it.

Now, when you specify no parameters or only those without explicit parameter-set tagging, the invocation succeeds, and $PSCmdlet.ParameterSetName reflects the name of the default parameter set.



来源:https://stackoverflow.com/questions/58822179/how-do-you-change-a-parameters-throw-message

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!