Powershell Switch parameter accepting argument - powershell

How can I make $m switch accept one argument but $prebuild doesn't need any argument.
Param (
[Parameter(Mandatory=$true)]
[Switch]$m,
[Switch]$prebuild
)
The script would be executed as below but I get the following error.
.\test.ps1 -m java
A positional parameter cannot be found that accepts argument 'java'

Change $m to a [string]:
param(
[string]$m,
[switch]$prebuild
)
To test whether an argument was passed by the caller, use the $PSBoundParameters automatic variable:
if($PSBoundParameters.ContainsKey('m')){
# An argument was passed to `-m`
}
(This last part is unnecessary if you keep the [Parameter(Mandatory=$true)] attribute)

Related

How to check Number of arguments in powershell?

param (
[string]$Name = $args[0],#First argument will be the adapter name
[IPAddress]$IP = $args[1],#Second argument will be the IP address
[string]$InterfaceId = $args[3],#Second argument will be the IP address
[string]$VlanId = $args[4], #Fourth argument will be vlanid
[string]$SubnetIP = $args[5],#subnet mask
[string]$IPType = "IPv4",
[string]$Type = "Static"
)
Write-Host $Args.Count
I want to check if command line arguments are supplied to the powershell script or not and if its not supplied then i want to show the usage by write. I am running the script in admin mode. I found one method after searching that using $Args.Count we can get the arguments count while running the script but its always zero for me. what am i doing wrong?
enter image description here
Get rid of the $args[x] assignments and add [cmdletbinding()] on top.
[CmdLetbinding()]
param (
[string]$Name, #First argument will be the adapter name
[IPAddress]$IP, # etc...
[string]$InterfaceId,
[string]$VlanId,
[string]$SubnetIP,
[string]$IPType = "IPv4",
[string]$Type = "Static"
)
Then you can use $PSBoundParameters.Count to get the argument count.
$args is a special variable that is used when named parameter are not present.
Therefore, since you have named parameter, it will always give you a count of zero (except maybe if you add more arguments than there is named parameters)
If you use a param block, then you don't need to assign $args[0] and others. In fact, this is totally useless as they will be $null.
The other approach, although I recommend you to keep the param block, is to not use any named parameters at all. In that case, $args will work as you expect it to.
[string]$Name = $args[0]
[IPAddress]$IP = $args[1]
[string]$InterfaceId = $args[3]
[string]$VlanId = $args[4]
[string]$SubnetIP = $args[5]
[string]$IPType = "IPv4"
[string]$Type = "Static"
The main difference is that if you have a param block, you can call your script in the following ways:
.\MyScript.ps1 -Name "Hello" -Ip 127.0.0.1
.\MyScript.ps1 "Hello" 127.0.0.1
Without the param block, you have only option #2 available to call the script.

How to pass a switch parameter to another PowerShell script?

I have two PowerShell scripts, which have switch parameters:
compile-tool1.ps1:
[CmdletBinding()]
param(
[switch]$VHDL2008
)
Write-Host "VHDL-2008 is enabled: $VHDL2008"
compile.ps1:
[CmdletBinding()]
param(
[switch]$VHDL2008
)
if (-not $VHDL2008)
{ compile-tool1.ps1 }
else
{ compile-tool1.ps1 -VHDL2008 }
How can I pass a switch parameter to another PowerShell script, without writing big if..then..else or case statements?
I don't want to convert the parameter $VHDL2008 of compile-tool1.ps1 to type bool, because, both scripts are front-end scripts (used by users). The latter one is a high-level wrapper for multiple compile-tool*.ps1 scripts.
You can specify $true or $false on a switch using the colon-syntax:
compile-tool1.ps1 -VHDL2008:$true
compile-tool1.ps1 -VHDL2008:$false
So just pass the actual value:
compile-tool1.ps1 -VHDL2008:$VHDL2008
Try
compile-tool1.ps1 -VHDL2008:$VHDL2008.IsPresent
Assuming you were iterating on development, it is highly likely that at some point you are going to add other switches and parameters to your main script that are going to be passed down to the next called script. Using the previous responses, you would have to go find each call and rewrite the line each time you add a parameter. In such case, you can avoid the overhead by doing the following,
.\compile-tool1.ps1 $($PSBoundParameters.GetEnumerator() | ForEach-Object {"-$($_.Key) $($_.Value)"})
The automatic variable $PSBoundParameters is a hashtable containing the parameters explicitly passed to the script.
Please note that script.ps1 -SomeSwitch is equivalent to script.ps1 -SomeSwitch $true and script.ps1 is equivalent to script.ps1 -SomeSwitch $false. Hence, including the switch set to false is equivalent to not including it.
According to a power shell team's blog (link below,) since V2 there is a technique called splatting. Basically, you use the automatic variable #PsBoundParameters to forward all the parameters. Details about splatting and the difference between # and $ are explained in the Microsoft Docs article (link below.)
Example:
parent.ps1
#Begin of parent.ps1
param(
[Switch] $MySwitch
)
Import-Module .\child.psm1
Call-Child #psBoundParameters
#End of parent.ps1
child.psm1
# Begin of child.psm1
function Call-Child {
param(
[switch] $MySwitch
)
if ($MySwitch){
Write-Output "`$MySwitch was specified"
} else {
Write-Output "`$MySwitch is missing"
}
}
#End of child.psm1
Now we can call the parent script with or without the switch
PS V:\sof\splatting> .\parent.ps1
$MySwitch is missing
PS V:\sof\splatting> .\parent.ps1 -MySwitch
$MySwitch was specified
PS V:\sof\splatting>
Update
In my original answer, I sourced the children instead of importing it as a module. It appears sourcing another script into the original just makes the parent's variables visible to all children so this will also work:
# Begin of child.ps1
function Call-Child {
if ($MySwitch){
Write-Output "`$MySwitch was specified"
} else {
Write-Output "`$MySwitch is missing"
}
}
#End of child.ps1
with
#Begin of parent.ps1
param(
[Switch] $MySwitch
)
. .\child.ps1
Call-Child # Not even specifying #psBoundParameters
#End of parent.ps1
Maybe, this is not the best way to make a program, nevertheless, this is the way it works.
About Splatting(Microsoft Docs)
How and Why to Use Splatting (passing [switch] parameters)
Another solution. If you declare your parameter with a default value of $false:
[switch] $VHDL2008 = $false
Then the following (the -VHDL2008 option with no value) will set $VHDL2008 to $true:
compile-tool1.ps1 -VHDL2008
If instead you omit the -VHDL2008 option, then this forces $VHDL2008 to use the default $false value:
compile-tool1.ps1
These examples are useful when calling a Powershell script from a bat script, as it is tricky to pass a $true/$false bool from bat to Powershell, because the bat will try to convert the bool to a string, resulting in the error:
Cannot process argument transformation on parameter 'VHDL2008'.
Cannot convert value "System.String" to type "System.Management.Automation.SwitchParameter".
Boolean parameters accept only Boolean values and numbers, such as $True, $False, 1 or 0.

How to define a function which requires one of two parameters?

For the following function, it must have a parameter of Group of type int or a parameter of Items of type int array. How to define the function? It must have one of these parameters but cannot have both.
Start-Execute -Group 1
Start-Execute -Items 100,200,300
You want to use Parameter Sets, which are a feature of Advanced Functions.
function Start-Execute {
[CmdletBinding()]
param(
[Parameter(
ParameterSetName='ByGroup',
Mandatory=$true
)]
[int]
$Group ,
[Parameter(
ParameterSetName='ByItems',
Mandatory=$true
)]
[int[]]
$Items
)
# function code
}
Inside the function, you can determine which parameter set was specified by testing the value of $PSCmdlet.ParameterSetName to see which parameter set it matches.
To see that your parameter sets were created correctly, run the following after the function definition has been executed:
Get-Help Start-Execute
That will show you separate invocations for each parameter set.
That being said, we can't tell what your function does. If $Items is just meant to be an array of multiple $Groups (that is, a single item is the same as a group), then your function should accept a single int array [int[]] and then just always process it with foreach, because that will work correctly even with a single value supplied.
Adding a parameter to multiple parameter sets.
You asked about adding a parameter called -Debug. I just want to point out that -Debug is a Common Parameter, so you probably shouldn't use that name. I'll show an example using a parameter named -Test:
function Start-Execute {
[CmdletBinding()]
param(
[Parameter(
ParameterSetName='ByGroup',
Mandatory=$true
)]
[int]
$Group ,
[Parameter(
ParameterSetName='ByItems',
Mandatory=$true
)]
[int[]]
$Items ,
[Switch]
$Test
)
)
# function code
}
This is one way to do it: don't provide any parameter set names. It will be available in all sets.
Another way is to provide a separate [Parameter()] attribute for each parameter set:
[Parameter(
ParameterSetName='ByItems',
Mandatory=$true
)]
[Parameter(
ParameterSetName='ByGroup',
Mandatory=$false
)]
[Switch]
$Test
This is useful when you want to use different settings for different sets, such as to make this parameter mandatory in one parameter set but optional in another, or to make the parameter available to multiple but not all sets.

Accepting an optional parameter only as named, not positional

I'm writing a PowerShell script that's a wrapper to an .exe. I want to have some optional script params, and pass the rest directly to the exe. Here's a test script:
param (
[Parameter(Mandatory=$False)] [string] $a = "DefaultA"
,[parameter(ValueFromRemainingArguments=$true)][string[]]$ExeParams # must be string[] - otherwise .exe invocation will quote
)
Write-Output ("a=" + ($a) + " ExeParams:") $ExeParams
If I run with the a named param, everything is great:
C:\ > powershell /command \temp\a.ps1 -a A This-should-go-to-exeparams This-also
a=A ExeParams:
This-should-go-to-exeparams
This-also
However, if I try to omit my param, the first unnamed param is assigned to it:
C:\ > powershell /command \temp\a.ps1 This-should-go-to-exeparams This-also
a=This-should-go-to-exeparams ExeParams:
This-also
I would expect:
a=DefaultA ExeParams:
This-should-go-to-exeparams
This-also
I tried adding Position=0 to the param, but that produces the same result.
Is there a way to achieve this?
Maybe a different parameter scheme?
By default, all function parameters are positional. Windows PowerShell assigns position numbers to parameters in the order in which the parameters are declared in the function. To disable this feature, set the value of the PositionalBinding argument of the CmdletBinding attribute to $False.
have a look at How to disable positional parameter binding in PowerShell
function Test-PositionalBinding
{
[CmdletBinding(PositionalBinding=$false)]
param(
$param1,$param2
)
Write-Host param1 is: $param1
Write-Host param2 is: $param2
}
The main answer still works in version 5 (according to comments, it may have been broken for a while in version 2).
There is another option: add Position to the ValueFromRemainingArgs parameter.
Sample CommandWrapper.ps1:
param(
$namedOptional = "default",
[Parameter(ValueFromRemainingArguments = $true, Position=1)]
$cmdArgs
)
write-host "namedOptional: $namedOptional"
& cmd /c echo cmdArgs: #cmdArgs
Sample output:
>commandwrapper hello world
namedOptional: default
cmdArgs: hello world
This appears to follow from PowerShell assigning parameter positions from the first parameter with a Position designated.

Powershell Cmdlet with Mandatory Parameters

I'm trying to create a simple powershell cmdlet that would have a few mandatory parameters. I've found the following code for doing so however, I cannot get it to execute:
function new-command() {
[CmdletBinding()]
PARAM (
[Parameter(Mandatory=$true)]
[string]$Name
)
}
new-command
Returns the following error:
Missing closing ')' in expression." Line: 5 Char: 3 + [ <<<< string]$Name
What am I doing wrong?
The explanation is that you are running this script in PowerShell V1.0 and these function attributes are supported in PowerShell V2.0. Look at $host variable for you PowerHhell version.
Try this instead:
function new-command {
[CmdletBinding()]
PARAM (
[Parameter(Mandatory=$true)]
[string]$Name
)
}
new-command
You don't need parentheses after the function name.
In PS 2.0 mandatory parameters are controlled through the CmdLetBinding and Parameter attributes as shown in the other answers.
function new-command {
[CmdletBinding()]
PARAM (
[Parameter(Mandatory=$true)]
[string]$Name
)
$Name
}
new-command
In PS 1.0 there are not direct constructs for handling mandatory attributes but you can for example throw an error if a mandatory parameter hasn't been supplied. I often use the following construct.
function new-command {
param($Name=$(throw "Mandatory parameter -Name not supplied."))
$Name
}
I hope this helps.
You'll have the same error message even with Powershell v2.0 if Param(...) hasn't been declared at the beginning of the script (exclude comment lines). Please refer to powershell-2-0-param-keyword-error
Try below syntax and also kindly check whether have missed any double quotes or brackets.
Param([parameter(Mandatory=$true, HelpMessage="Path to file")] $path)