Passing Parameters to a Script in an Executable File

This is the first blog in a multi-part series about designing a Windows PowerShell script that will be packaged in an executable file.

—–

PowerShell Studio and PrimalScript have great support for packing scripts in executable files. Windows PowerShell is an interpreted language, so you cannot compile it, but you can wrap scripts (.psm1) and simple modules (.psm1) files in an executable file

I typically build executable files for GUI apps don’t have parameters, but you can build executable files for any script, including scripts that take parameters.

However, passing parameters to a script inside a native .exe is not simple, because executable files don’t have built-in features to support objects other than strings or to support PowerShell’s parameter name and parameter value syntax.

In the past, all parameters had to be positional, users had to enter parameter values without names, and enter them in the specified order, or your script would have to parse the input string and associate the values with the correct parameters.

But, a new feature of PowerShell Studio (beginning in 4.2.96) and PrimalScript (beginning in 7.1.72), makes this much easier. You are still limited to strings values (and types that can parse string values). But, users can now enter parameter names and values, just as they do for a script. And new code in the executable file that PowerShell Studio and PrimalScript builds parses the input string and associates the parameter values with the correct parameters, so you don’t have to.

New input parsing for scripts in .exe files

An executable file can pass a string or an array of string values to the PowerShell script inside. It can’t handle any other value type. So, the first rule of writing scripts for exes is to use only parameters that take string values.

Let’s start with a very simple script, New-WordTree.ps1, that repeats a specified word for an increasing number of specified times (default = 1). (Credit to PowerShell MVP Doug Finke for the neat idea.) The script has a string parameter, Word, and an integer parameter, Number.

Param
(
    [Parameter(Mandatory = $true)]
    [String]$Word,
    [int]$Number = 1
)
1..$Number | foreach { "$Word" * $_}

When you run this script as a .ps1 file, you can use parameter names and values.

PS C:\> .\New-WordTree.ps1 -Word PowerShell -Number 3
PowerShell
PowerShellPowerShell
PowerShellPowerShellPowerShell

When you wrap this script in an executable file and use the same command line, it used to fail, because all input strings were interpreted as parameter values. In this case, ‘-Parameter’ was interpreted as the value of the Word parameter and ‘PowerShell’ was interpreted as the value of the Number parameter. The script failed when Windows PowerShell couldn’t convert ‘PowerShell’ to a integer.

PS C:\> .\New-WordTree.exe -Word PowerShell -Number 3 
Line 1: A positional parameter cannot be found that accepts argument 'PowerShell'.PS C:\>

But, the new input string parsing feature of PowerShell Studio and PrimalScript parses the input string for you so commands with parameter names and parameter values work correctly. You do not need to give the user special instructions or do any parsing in your script.

PS C:\> .\New-WordTree.exe -Word PowerShell -Number 3
PowerShell 
PowerShellPowerShell 
PowerShellPowerShellPowerShell

I can even enter the parameters in an unspecified order, just as I can do in a script.

PS C:\> .\New-WordTree.exe  -Number 3 -Word PowerShell
PowerShell
PowerShellPowerShell
PowerShellPowerShellPowerShell

If the parameters are positional, which they are by default in a script or function with only one parameter set, you can omit parameter names, just as you can in a script.

PS C:\> .\New-WordTree.exe PowerShell 3
PowerShell
PowerShellPowerShell
PowerShellPowerShellPowerShell

Change Parameter Values to Strings

When writing a script that will be wrapped in an exe, use parameters that take string values. When you convert existing scripts for use in an exe, remember to change parameter input types to strings.

You don’t need to rely solely on string parameters . Notice that the Number parameter in New-WordTree takes an Integer value, but you can use it in an executable file, because Windows PowerShell automatically converts the values from String to Integer for you. This conversion works for System.Double, too. For more complex objects, like process objects (System.Diagnostics.Process), the conversion associates only the name of the process with a string parameter. Its utility depends on what you intend to do with the object inside your script.

This version of the New-WordTree.ps1 script has a Switch parameter that adds a space between the repeated words.

Param
(
    [Parameter(Mandatory = $true)]
    [String]$Word,
 
    [int]$Number = 1,
 
    [Switch]$AddSpace
)
 
$space = ''
if ($AddSpace){ $space = ' ' }
 
1..$Number | foreach { "$Word$space" * $_}

I can run this as a script.

PS C:\> ."C:\ps-test\New-WordTree.ps1" -Word PowerShell -Number 3
PowerShell
PowerShellPowerShell
PowerShellPowerShellPowerShell

PS C:\> ."C:\ps-test\New-WordTree.ps1" -Word PowerShell -Number 3 -AddSpace
PowerShell
PowerShell PowerShell
PowerShell PowerShell PowerShell

But, if I run it as an executable file, the AddSpace switch parameter is ignored.

PS C:\> .\New-WordTree.exe -Word PowerShell -Number 3 -AddSpace
PowerShell 
PowerShellPowerShell 
PowerShellPowerShellPowerShell

If I add a value for the AddSpace parameter, such as $True, the command fails, because Windows PowerShell cannot convert the string to a type that the Switch parameter accepts.

PS C:\> .\New-WordTree.exe -Word PowerShell -Number 3 -AddSpace $True
Line 1: A positional parameter cannot be found that accepts argument 'True'.PS C:\>

PS C:\> .\New-WordTree.exe -Word PowerShell -Number 3 -AddSpace "$True"
Line 1: A positional parameter cannot be found that accepts argument 'True'.PS C:\>

PS C:\> .\New-WordTree.exe -Word PowerShell -Number 3 -AddSpace:$True
Line 1: A parameter cannot be found that matches parameter name 'Addspace:True'.PS C:\>

Instead, you need to change the switch parameter to a type that takes a string value. In this example, I change the AddSpace switch parameter to a string parameter that takes values of True and False. I also change the IF statement to interpret the new values.

Param
(
    [Parameter(Mandatory = $true)]
    [String]$Word,
 
    [int]$Number = 1,
 
    [ValidateSet('True', 'False')]
    [string]$AddSpace                #Change Switch to String
)
$space = ''
if ($AddSpace -eq 'True'){ $space = ' ' }
 
1..$Number | foreach { "$Word$space" * $_}

When I package this script in an executable, you can now run it by submitting a string value.

PS C:\> .\New-WordTree.exe -Word PowerShell -Number 3 -AddSpace True
PowerShell
PowerShell PowerShell
PowerShell PowerShell PowerShell

PS C:\> .\New-WordTree.exe -Word PowerShell -Number 3 -AddSpace $True
PowerShell
PowerShell PowerShell
PowerShell PowerShell PowerShell

Handling Multiple Values for a Parameter

If any script in an executable file has a parameter that takes multiple values, like a collection of strings or integers, the end-user must submit the value collection in a single comma-separated string enclosed in quotation marks. Then, in your script, you need to split the quoted string.

Because the quoted string is submitted to the executable, not to Windows PowerShell, the standard PowerShell rules for quoted strings do not apply.

For example, in this version of New-WordTree.exe, the Word parameter takes multiple words.

[Parameter(Mandatory = $true)]
[String[]]$Word

To call the script in an exe, the user must enclose the value collection in single or double quotes. (This is unusual, so be sure to show the user how to do this in your help topic.)

PS C:\> .\New-WordTree.exe -Word "PowerShell, NanoServer, DSC" -Number 3 -AddSpace True
PowerShell
PowerShell PowerShell
PowerShell PowerShell PowerShell
NanoServer
NanoServer NanoServer
NanoServer NanoServer NanoServer
DSC
DSC DSC
DSC DSC DSC

To handle these quoted strings in the script, you have to split the parameter value string on each comma. Also, to get rid of spaces, I call the Trim() method of strings. It’s a good habit to develop.

$words = ($Word -split ',').Trim()

And, as you would in any script, you need to change your code to to handle multiple values, such as using a ForEach statement.

foreach ($item in $words)
{
 
}

Here’s the result.

Param
(
    [Parameter(Mandatory = $true)]
    [String[]]$Word,
 
    [int]$Number = 1,
 
    [ValidateSet('True', 'False')]
    [string]$AddSpace
)
 
# Split the parameter value array into strings
$words = ($Word -split ',').Trim()
 
foreach ($item in $words)
{
    $space = ''
    if ($AddSpace -eq 'True')
    { $space = ' ' }
 
    1..$Number | foreach { "$item$space" * $_ }
}

This once-difficult task of parsing input in an exe-wrapped script is now much easier. But, if you need to parse the input string manually, you can do that, too. In the a href=”https://wp.me/p3tXTf-2Hg” target=”_blank”>next post, we’ll display help for an executable file.

June Blender is a technology evangelist at SAPIEN Technologies, Inc. You can reach her at juneb@sapien.com or follow her on Twitter at @juneb_get_help.Parsing Parameters for a Script in an Executable File