Mutually exclusive powershell parameters

powershell parameter set one or the other
powershell parameter sets
powershell multiple parameter sets
powershell switch parameter
powershell global parameters
cannot bind positional parameters because no names were given
powershell function parameters parametersetname
powershell cmdletbinding multiple parameters
SCENARIO
  • I'm writing a cmdlet for Powershell 2.0 using Visual Studio 2008 and .NET 3.5
  • the cmdlet requires 3 arguments.

my intended grammar of the cmdlet is something like this:

cmdletname [foo|bar] p1, p2
  • That reads as the user must give a value for "-foo" or "-bar" but can't give both together.
EXAMPLE OF VALID INPUT
cmdletname -foo xxx -p1 hello  -p2 world
cmdletname -bar yyy -p1 hello  -p2 world
EXAMPLE OF INVALID INPUT
cmdletname -foo xxx -bar yyy -p1 hello  -p2 world
MY QUESTION
  • My question is on how to do this in powershell so that it does all the checking for me - or if that is possible at all.
  • I know I can use just have two optional parameters for foo and bar and simply do the error checking manually. That's how I have it implemented currently.
  • Alternatively, I am interested in suggestions for different approaches.

Here's an example of using ParameterSetName taken from a cmdlet in the PowerShell Community Extensions. BTW, for ideas you can browse the PSCX source code.

[Cmdlet(VerbsCommon.Set, PscxNouns.Clipboard, 
        DefaultParameterSetName = ParamSetText)]
[Description("Copies the item in the system clipboard.")]
[RelatedLink(typeof(GetClipboardCommand))]
[RelatedLink(typeof(OutClipboardCommand))]
[RelatedLink(typeof(WriteClipboardCommand))]
public class SetClipboardCommand : ClipboardCommandBase
{
    ... fields elided

    const string ParamSetRtf = "Rtf";
    const string ParamSetHtml = "Html";
    const string ParamSetText = "Text";
    const string ParamSetFiles = "Files";
    const string ParamSetImage = "Image";
    . 
    [AllowNull]
    [Parameter(ValueFromPipeline = true, ParameterSetName = ParamSetImage)]
    public Image Image { get; set; }
    . 
    [AllowNull]
    [AllowEmptyCollection]
    [Parameter(ValueFromPipeline = true, ValueFromRemainingArguments = true,
               ParameterSetName = ParamSetFiles)]
    public FileSystemInfo[] Files { get; set; }
    . 
    [AllowNull]
    [AllowEmptyString]
    [Parameter(ValueFromPipeline = true, ValueFromRemainingArguments = true,
               ParameterSetName = ParamSetText)]
    public string Text { get; set; }
    . 
    [Parameter(ValueFromPipeline = true, ValueFromRemainingArguments = true,
               ParameterSetName = ParamSetHtml)]
    public string Html { get; set; }
    .         
    [Parameter(ValueFromPipeline = true, ValueFromRemainingArguments = true,
               ParameterSetName = ParamSetRtf)]
    public string Rtf { get; set; }
    . 
    protected override void ProcessRecord()
    {
        ...
    }
    .
    protected override void EndProcessing()
    {
        ExecuteWrite(delegate
        {
            switch (ParameterSetName)
            {
                case ParamSetFiles:
                    if (Paths.Count == 0)
                        WinFormsClipboard.Clear();
                    else
                        WinFormsClipboard.SetFileDropList(_paths);
                    break;

                case ParamSetImage:
                    if (Image == null)
                        WinFormsClipboard.Clear();
                    else
                        WinFormsClipboard.SetImage(_image);
                    break;

                case ParamSetRtf:
                    SetTextContents(Rtf, TextDataFormat.Rtf);
                    break;

                case ParamSetHtml:
                    SetTextContents(Html, TextDataFormat.Html);
                    break;

                default:
                    SetTextContents(Text, TextDataFormat.UnicodeText);
                    break;
            }
        });
    }
    ...
}

Note that the cmdlet typically declares a default ParameterSetName that helps PowerShell determine the "default" parameter set to use when there is ambiguity. Later on, if needed, you can determine which parameter set is in force by querying this.ParameterSetName as the switch statement does above in the EndProcessing() override.

Mutually exclusive powershell parameters, Here's an example of using ParameterSetName taken from a cmdlet in the PowerShell Community Extensions. BTW, for ideas you can browse  Sometimes, PowerShell functions have parameters that should be mutually exclusive: the user should only be able to use either one, not both. To create mutually exclusive parameters, assign them to different parameter sets, and make sure you define a default parameter set name

You can use the parameter attribute to declare multiple parameter sets. You then simply assign parameters that are mutually exclusive to different parameter sets.

EDIT:

This is also documented in 'about_Functions_Advanced_Parameters', under the section "ParameterSetName Named Argument". This is how different sets of parameters is handled with cmdlets like Get-Random (which has mutually exclusive parameters):

> get-random -input 4 -max 77
Get-Random : Parameter set cannot be resolved using the specified named parameters.
At line:1 char:11
+ get-random <<<<  -input 4 -max 77
    + CategoryInfo          : InvalidArgument: (:) [Get-Random], ParameterBindingException
    + FullyQualifiedErrorId : AmbiguousParameterSet,Microsoft.PowerShell.Commands.GetRandomCommand

Here's an example of doing it in a function:

function exclusive_params() { 
    param( 
        [parameter(ParameterSetName="seta")]$one,
        [parameter(ParameterSetName="setb")]$two, 
        $three 
    )
    "one: $one"; "two: $two"; "three: $three" 
}

The parameters one and two are in different parameter sets, so they cannot be specified together:

> exclusive_params -one foo -two bar -three third
exclusive_params : Parameter set cannot be resolved using the specified named parameters.
At line:1 char:17
+ exclusive_params <<<<  -one foo -two bar -three third
    + CategoryInfo          : InvalidArgument: (:) [exclusive_params], ParameterBindingException
    + FullyQualifiedErrorId : AmbiguousParameterSet,exclusive_params

Which is the same error I got with Get-Random. But I can use the parameters independently:

> exclusive_params -one foo -three third
one: foo
two:
three: third

...or:

> exclusive_params -two bar -three third
one:
two: bar
three: third

PowerShell functions and Parameter Sets, Both parameters exists but are mutually exclusive, you cannot use them both at the same time, since they are defined in two different Parameter  Mutually Exclusive Parameters and Properties ParameterSets were introduced in PowerShell v2. This allowed definition of different sets of parameters for advanced functions.

I came here but with an additional requirement: Optional mutual exclusive parameters.

This post here helped me to find half of the answer. So I thought to post here the full answer in case someone has the same requirements.

The code below can be used at the top of a Powershell script to have 4 optional parameters of which LaunchAsAdmin and LaunchAsCouponBrowser are mutually exclusive while token and WorkstationName are also optional but can be combined with any other parameter.

[CmdletBinding(DefaultParametersetName="default")]                  
Param(
    [string]$token,
    [string]$WorkstationName,
    [parameter(ParameterSetName="seta")][switch]$LaunchAsAdmin,
    [parameter(ParameterSetName="setb")][switch]$LaunchAsCouponBrowser  
)

CmdLet design advice, I'm building a set of Powershell cmdlets for our product and I want to parameters which are made mutually exclusive via parameter sets. PowerShell script with Multiple Groups of Mutually Exclusive Parameters I honestly think what I am trying to do is a limitation within the way parameters work in PowerShell, since there is no way to explicitly exclude a parameter based on another parameter set.

Parameters Should Support Mutual Exclusion Natively · Issue #5175 , The problem arises when you have mutually exclusive parameters a proposal to solve this https://github.com/PowerShell/PowerShell-RFC/  This can get wildly out of hand if you have a third or fourth set of mutually exclusive parameters. This becomes difficult to maintain and to add additional parameters. Another option is to have logic in the body of the function to check for mutually exclusive Parameters at the cost of the mutual exclusion not being as discoverable.

Topic: Powershell function forcing only ONE switch, Parameter sets are definitely the way to go. PowerShell v6 may get the option of declaring 2 parameters to be mutually exclusive but the way  Mutually Exclusive Experimental Features There are cases where an experimental feature cannot co-exist side-by-side with an existing feature or another experimental feature. For example, you can have an experimental cmdlet that overrides an existing cmdlet.

Always Explicitly Set Your Parameter Set Variables For PowerShell , Parameter sets were introduced in PowerShell v2.0 and are useful for enforcing mutually exclusive parameters on a cmdlet. Ed Wilson  Both parameters exists but are mutually exclusive, you cannot use them both at the same time, since they are defined in two different Parameter Sets. First some basics A parameter set is defined in the [Parameter()] block of a Parameter.

Comments
  • very nice, answered my question precisely, I've no idea why this has not been voted up more!