views:

347

answers:

2

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.
+1  A: 

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

At least that's what I took from MSDN and the reflected code of Get-Random (which has mutually exclusive parameters).

Joey
+5  A: 

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.

Keith Hill