Programmatic way to get valid string values for a parameter

<WIZARD WARNING>  ISVs and Tool vendors may find this important, Advanced users may find this interesting, Casual users may want to skip this.


Someone asked me if there was a programmatic way to tell what the legal values for a string parameter to a cmdlet was.  The example was OUT-FILE -ENCODING xxx


Out current help doesn’t tell you this information (and it wouldn’t be a great programmatic experience even if it did) and neither does Get-Command:


PS> get-command out-file |fl Definition
Definition : Out-File [-FilePath] <String> [[-Encoding] <String>] [-Append]
              [-Force] [-NoClobber] [-Width <Int32>] [-InputObject <PSObjec
             t>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-Er
             rorVariable <String>] [-OutVariable <String>] [-OutBuffer <Int
             32>] [-WhatIf] [-Confirm]


It tells me that it takes a string but not what the values are.  When you try to assign an incorrect value, you’ll be told what the correct values are:



PS> gps |out-file foo.txt -Encoding UTF-8
Out-File : Cannot validate argument “UTF-8″ because it does not belong to t
he set “unicode, utf7, utf8, utf32, ascii, bigendianunicode, default, oem”.
At line:1 char:32
+ gps |out-file foo.txt -Encoding  <<<< UTF-8


That is fine for the interactive case but what about the programmatic case – can you find this info out?  The answer is: maybe. 


We designed PowerShell (formerly known as Monad) for this scenario. When a cmdlet developer declares their parameters, we provide them a set of VALIDATION attributes that we encourage them to use.  At first glance, a developer might say – the heck with those, it would be just as simple to code this myself.  BUT WAIT — DON’T DO THAT. 


      PSTIP: Use the Attributes Luke


There are 2 primary benefits of using the attributes instead of coding them yourself



  1. PowerShell engine does the work and will generate the error messages.  That might sound trivial but I can guarantee you that if 100 cmdlet developers write their own code, you’ll get at least 200 error messages for the exact same condition. Using the Attributes ensures a consistent user experience. 

    Don’t care about your user’s experience?  Then there is an economic advantage to using the attributes.  Use them and Microsoft will pick up the bill for translating them into all the languages your customers want.  (Consistency through economics – do you see the pattern here?)

  2. The attributes provide a programmable way to discover the capabilities of the cmdlet.  This can be used in all sorts of wonderful ways – e.g. autogenerating GUI front end to the cmdlets.  If we know what the parameters are and we also know what the valid values are, we can turn that into a combo-box instead of having the user TYPE the values.

If Cmdlet developers don’t do use the Attributes, you get none of this. 


Let’s explore the CmdletInfo datastructure. This is what is returned when you do a Get-Command (gcm) on a cmdlet. 


PS> gcm out-file |gm param*
   TypeName: System.Management.Automation.CmdletInfo


Name          MemberType Definition
—-          ———- ———-
ParameterSets Property   System.Collections.ObjectModel.ReadOnlyCollecti…


ParameterSets are the Sets of valid parameters (are we good at naming or what?).  For instance, Get-Process (gps) can be provided an -ID parameter or a -NAME parameter.  These are 2 ParameterSets for that cmdlet.


PS> gps -Name powershell


Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
——-  ——    —–      —– —–   ——     — ———–
   1112      15   112816      19812   250   206.61    936 powershell



PS> gps -ID 936


Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
——-  ——    —–      —– —–   ——     — ———–
   1119      15   112920      19984   250   206.66    936 powershell


Out-File has only one parameterSet


PS> (gcm out-file).Parametersets |fl name
Name : __AllParameterSets


ParameterSets have a set of Parameters.  One of those is the Encoding parameter:



PS> $p=(gcm out-file).Parametersets[0].parameters |?{$_.name -eq “Encoding”}


PS> $p
Name                            : Encoding
ParameterType                   : System.String
IsMandatory                     : False
IsDynamic                       : False
Position                        : 1
ValueFromPipeline               : False
ValueFromPipelineByPropertyName : False
ValueFromRemainingArguments     : False
HelpMessage                     :
Aliases                         : {}
Attributes                      : {System.Management.Automation.ValidateSet
                                  Attribute, __AllParameterSets, System.Man
                                  agement.Automation.ValidateNotNullOrEmpty
                                  Attribute}


Notice that this also tells you if the parameter has an alias, is positional, etc.  That parameter has a set of Attributes:


PS> $p.Attributes |ft Typeid


TypeId
——
System.Management.Automation.ValidateSetAttribute
System.Management.Automation.ParameterAttribute
System.Management.Automation.ValidateNotNullOrEmptyAttribute


The ValidateSetAttribute has the list of valid strings for this Parameter:



PS> ($p.Attributes|? {$_.Typeid -match “ValidateSetAttribute” }).ValidValues
unicode
utf7
utf8
utf32
ascii
bigendianunicode
default
oem


And there you have it.  A little chewy yes but I don’t expect end-users to ever care about this but system programmers and tool vendors will.


Enjoy!
Jeffrey Snover [MSFT]
Windows PowerShell Architect


PSMDTAG:FAQ: Is there a programmatic way to get valid string values for a parameter?
PSMDTAG:PHILOSOPHY: Use Attributes instead of code for validating parameters.
PSMDTAG:INTERNAL: walk through of the Parameter data for cmdlets