Archive for the ‘Microsoft Windows 11’ Category
Switch or Parameter
I told my students that processing parameters in pairs, like a prior post demonstrated for another student, was a bad idea. That’s true because any list of parameters may contain switches and parameter/argument pairs.
A switch is a signal to turn on or off some behavior, like -v typically makes a utility produce a verbose (or wordy) display to console. Parameter and argument pairs are like name and value pairs in dictionaries. For example, you may have the following:
-o output.csv -s query.sql |
The dash (–) identifies the parameter and the lack of one identifies an argument or value. So, here’s simply the PowerShell block re-written to demonstrate how to handled an argument list that may contain switches and parameter/argument pairs:
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | # Wrap the Parameter call to avoid a type casting warning. try { param ( [Parameter(Mandatory)][hashtable]$args ) } catch {} # Check for switches and parameters with arguments. for ($i = 0; $i -lt $args.count; $i += 1) { if (($args[$i].startswith("-")) -and ($args[$i + 1].startswith("-"))) { $verbose = $true # Print to verbose console. if ($verbose) { Get-Message $args[$i] }} elseif ($args[$i].startswith("-")) { # Print to verbose console. if ($verbose) { Get-Message $args[$i] $args[$i + 1] } # Evaluate and take action on parameters and values. if ($args[$i] -eq "-o") { $outfile = $args[$i + 1] } elseif ($args[$i] -eq "-q") { $sqlFile = $args[$i + 1] } elseif ($args[$i] -eq "-s") { # You must evaluate the argument before using it to access an enum # value; and the program assumes an incorrect SQL statement value # means you should assume the SQL statemente is a query. if ([SQLStatements]::($args[$i + 1])) { $stmt = [SQLStatements]::($args[$i + 1]) }} elseif ($args[$i] -eq "-p") { $path = $args[$i + 1] } } } |
I hope this helps those looking for a solution to processing a parameter list in a PowerShell script.
PowerShell Parameter List
Playing around with passing parameters for a PowerShell project and ran into some interesting quirks. I found that you can pass in a set of values by declaring the parameter list as an array or string[] but the Microsoft documentation suggests using the array data type, which is implicitly cast as a string.
So, this would work provided you want to enter commas between the parameters:
param ( [Parameter(Mandatory)][array]$args ) |
However, you can’t use a -o for example because it’ll raise this error message:
C:\PowerShell\test\Params.ps1 : Parameter cannot be processed because the parameter name 'o' is ambiguous. Possible matches include: -OutVariable -OutBuffer. At line:1 char:21 + ./Params.ps1 -o output.csv + ~~ + CategoryInfo : InvalidArgument: (:) [Params.ps1], ParameterBindingException + FullyQualifiedErrorId : AmbiguousParameter,Params.ps1 |
The error doesn’t occur because the script but because the -OutVariable and -OutBuffer are parameters of the Powershell executable. You can get rid of the error and use an array without error by using two hyphens (—) back-to-back before your script’s parameters. The two hyphens together are the end-of-parameters operator, which means everything following it will be treated as arguments.
However, the better solution is to use a hashtable, like:
param ( [Parameter(Mandatory)][hashtable]$args ) |
However, as written above it raises the following error:
Cannot convert the "System.Object[]" value of type "System.Object[]" to type "System.Collections.Hashtable". At C:\PowerShell\test\Params.ps1:35 char:1 + param ( + ~~~~~~~ + CategoryInfo : InvalidArgument: (:) [], RuntimeException + FullyQualifiedErrorId : ConvertToFinalInvalidCastException |
There’s a great deal of humor if you recognize that the parameters are actually cast as strings in the hashtable and the error is more of a warning rather than a real error. All you need to do is wrap the param() call in a try-catch block, like:
try { param ( [Parameter(Mandatory)][hashtable]$args ) } catch {} |
Then, you can call the Powershell script like this from the CLI:
powershell params -o outfile.csv -q query.sql |
and, process it in a block like this, naturally qualifying the keys before assigning to local variables:
if ($args.count % 2) { Write-Host "Uneven number of parameters." Exit } else { for ($i = 0; $i -lt $args.count; $i += 2) { if ($args[$i] -eq "-o") { $outFile = $args[$i + 1] } elseif ($args[$i] -eq "-q") { $queryFile = $args[$i + 1] } } } |
Since this is really simply a script file, I prefer the if-not logic or a verbose (wordy) list of options when the parameter list is incorrect. For example, you could rewrite the block above to exclude an error message as follows:
if (!($args.count % 2)) { for ($i = 0; $i -lt $args.count; $i += 2) { if ($args[$i] -eq "-o") { $outFile = $args[$i + 1] } elseif ($args[$i] -eq "-q") { $queryFile = $args[$i + 1] } }} else { exit } |
While I answered the question about pairs, it’s a bad approach. You can find the right way to handle options (or, switches) and parameters to your PowerShell scripts in this later blog post.
As always, I hope blogging tidbits helps others looking for a solution.