Programmatic way to get valid string values for a parameter

PowerShell Team

<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

2 comments

Discussion is closed. Login to edit/delete existing comments.

  • dan henry 0

    Great article and exactly what I needed today – I wanted to extract the valid values of hash algorithms supported by Get-Filehash programmatically so my script would alsways use the right values without me having to hard code them.
     
    One problem I had i nyour code was that I have to use ValidateSet instead of ValidateSetAttribute for the final extraction of ValidValues.  here’s my code:
     
    #Grab the “Get-Filehash -Algorithm” parameter object
    $getFileHashParams = (Get-Command Get-Filehash).Parametersets[0].parameters |?{$_.name -eq “Algorithm”}
     
     
    #Grab the valid values for the parameter “Get-Filehash -Algorithm” from its ValidateSet
    $algorithms = @(($getFileHashParams.Attributes|? {$_.TypeId -match “ValidateSet” }).ValidValues)

  • dan henry 0

    Great article and exactly what I needed today – I wanted to extract the valid values of hash algorithms supported by Get-Filehash programmatically so my script would alsways use the right values without me having to hard code them.
     
    One problem I had using your code was that I had to use ValidateSet instead of ValidateSetAttribute for the final extraction of ValidValues.  here’s my code:
     
    #Grab the “Get-Filehash -Algorithm” parameter object
    $getFileHashParams = (Get-Command Get-Filehash).Parametersets[0].parameters |?{$_.name -eq “Algorithm”}
     
     
    #Grab the valid values for the parameter “Get-Filehash -Algorithm” from its ValidateSet
    $algorithms = @(($getFileHashParams.Attributes|? {$_.TypeId -match “ValidateSet” }).ValidValues)
     
     
    Thanks for the life-saver of an article, even if it’s from 2006!

Feedback usabilla icon