How to handle command-line arguments in PowerShell
You are reinventing the wheel. Normal PowerShell scripts have parameters starting with -
, like script.ps1 -server http://devserver
Then you handle them in a param
section (note that this must begin at the first non-commented line in your script).
You can also assign default values to your params, read them from console if not available or stop script execution:
param (
[string]$server = "http://defaultserver",
[Parameter(Mandatory=$true)][string]$username,
[string]$password = $( Read-Host "Input password, please" )
)
Inside the script you can simply
write-output $server
since all parameters become variables available in script scope.
In this example, the $server
gets a default value if the script is called without it, script stops if you omit the -username
parameter and asks for terminal input if -password
is omitted.
Update: You might also want to pass a "flag" (a boolean true/false parameter) to a PowerShell script. For instance, your script may accept a "force" where the script runs in a more careful mode when force is not used.
The keyword for that is [switch]
parameter type:
param (
[string]$server = "http://defaultserver",
[string]$password = $( Read-Host "Input password, please" ),
[switch]$force = $false
)
Inside the script then you would work with it like this:
if ($force) {
//deletes a file or does something "bad"
}
Now, when calling the script you'd set the switch/flag parameter like this:
.\yourscript.ps1 -server "http://otherserver" -force
If you explicitly want to state that the flag is not set, there is a special syntax for that
.\yourscript.ps1 -server "http://otherserver" -force:$false
Links to relevant Microsoft documentation (for PowerShell 5.0; tho versions 3.0 and 4.0 are also available at the links):
Aaron Wurthmann
Updated on July 08, 2022Comments
-
Aaron Wurthmann almost 2 years
What is the "best" way to handle command-line arguments?
It seems like there are several answers on what the "best" way is and as a result I am stuck on how to handle something as simple as:
script.ps1 /n name /d domain
AND
script.ps1 /d domain /n name.
Is there a plugin that can handle this better? I know I am reinventing the wheel here.
Obviously what I have already isn't pretty and surely isn't the "best", but it works.. and it is UGLY.
for ( $i = 0; $i -lt $args.count; $i++ ) { if ($args[ $i ] -eq "/n"){ $strName=$args[ $i+1 ]} if ($args[ $i ] -eq "-n"){ $strName=$args[ $i+1 ]} if ($args[ $i ] -eq "/d"){ $strDomain=$args[ $i+1 ]} if ($args[ $i ] -eq "-d"){ $strDomain=$args[ $i+1 ]} } Write-Host $strName Write-Host $strDomain
-
Keith Hill over 14 yearsIndeed one of PowerShell's big advantages is that it provides a standard parameter parsing infrastucture that is easy to use.
-
v.karbovnichy about 10 years@naivists, from PowerShell 2.0 instead of
[string]$username = $(throw "-username is required.")
there is syntax for mandatory parameters:[Parameter(Mandatory=$true)][string]$username
. Here is more info about difference between these techniques: blogs.technet.com/b/heyscriptingguy/archive/2011/05/22/… -
sheamus over 9 yearsBe wary of the bug when an arg is not supplied; powershell will just grab any extra text from the command line: .\yourscript.ps1 -server "serv" -password "mypass" typo This will magically assign 'typo' to $username.
-
kuga over 7 yearsThis is great! Could you make this a documentation? One question though: How to handle parameters with no
-option
tag? -
Logan over 7 yearsUsing a param block seems to have other unintended consequences though: stackoverflow.com/questions/40940819/…
-
Fernando Madruga over 6 years@sheamus: that is not a bug! Powershell will process and assign the arguments in the order they're given, unless overridden by using the proper parameter name, e.g., if your param block lists: $user $pass $server, and you execute yourscript.ps1 a b c, a will be set into $user, b into $pass and c into $server, UNLESS you specifically assign them! So, if you say: yourscript.ps1 -pass a b c, $pass will be set to a, and the remaining (unnamed) parameters will be used to fill in the missing ones, in the order listed in the parameter block, so $user = b, $server = c.
-
jpmc26 about 6 yearsThe better way to do username and password is to use the
Credential
type, which will take care of hiding the password during typing and storing it as aSecureString
. (I'm not totally sold on the value ofSecureString
, but at least it lowers the risk of it getting accidentally printed in the output.) -
Scrontch over 4 yearsGreat. But how to check if there are parameters given at all? In order to print a usage message for example.
-
naivists over 4 years@Scrontch, see this answer to a similar question: stackoverflow.com/a/48643616/239599
-
basickarl over 4 yearsWow, you couldn't of just made an example file?
-
Justin over 4 years@sheamus one way to avoid the "typo" scenario is to explicitly deny positional binding, this can be done per parameter or globally. just add [CmdletBinding(PositionalBinding=$false)] to the top of your cmdlet.
-
Timo over 3 yearsHere is a nice example for positional arguments which goes more along the linux path.
-
yoyo over 3 years@Timo awesome link, thanks, just what I needed!
[Parameter(ValueFromRemainingArguments=$True, Position=0)]
is the magic that prevents optional named parameters from consuming positional parameters. -
CODE-REaD about 2 yearsBeing a novice PowerShell programmer, I struggled for a good while with the error The term 'param' is not recognized as the name of a cmdlet, function, script file. The solution turned out to be simple: I can use this function only as the first non-commented line of code in my script!