How do I create an a statement with an inline If (IIf, see also: Immediate if or ternary If) in PowerShell?
If you also think that this should be a native Po
Function Compare-InlineIf
{
[CmdletBinding()]
Param(
[Parameter(
position=0,
Mandatory=$false,
ValueFromPipeline=$false
)]
$Condition,
[Parameter(
position=1,
Mandatory=$false,
ValueFromPipeline=$false
)]
$IfTrue,
[Parameter(
position=2,
Mandatory=$false,
ValueFromPipeline=$false
)]
$IfFalse
)
Begin{
Function Usage
{
write-host @"
Syntax
Compare-InlineIF [[-Condition] <test>] [[-IfTrue] <String> or <ScriptBlock>]
[[-IfFalse] <String> or <ScriptBlock>]
Inputs
None
You cannot pipe objects to this cmdlet.
Outputs
Depending on the evaluation of the condition statement, will be either the IfTrue or IfFalse suplied parameter values
Examples
.Example 1: perform Compare-InlineIf :
PS C:\>Compare-InlineIf -Condition (6 -gt 5) -IfTrue "yes" -IfFalse "no"
yes
.Example 2: perform IIF :
PS C:\>IIF (6 -gt 5) "yes" "no"
yes
.Example 3: perform IIF :
PS C:\>IIF `$object "`$true","`$false"
False
.Example 4: perform IIF :
`$object = Get-Item -ErrorAction SilentlyContinue "HKCU:\AppEvents\EventLabels\.Default\"
IIf `$object {`$_.GetValue("DispFilename")}
@mmres.dll,-5824
"@
}
}
Process{
IF($IfTrue.count -eq 2 -and -not($IfFalse)){
$IfFalse = $IfTrue[1]
$IfTrue = $IfTrue[0]
}elseif($iftrue.count -ge 3 -and -not($IfFalse)){
Usage
break
}
If ($Condition -IsNot "Boolean")
{
$_ = $Condition
} else {}
If ($Condition)
{
If ($IfTrue -is "ScriptBlock")
{
&$IfTrue
}
Else
{
$IfTrue
}
}
Else
{
If ($IfFalse -is "ScriptBlock")
{
&$IfFalse
}
Else
{
$IfFalse
}
}
}
End{}
}
Set-Alias -Name IIF -Value Compare-InlineIf
Powershell 7 allows ternary operators:
$message = (Test-Path $path) ? "Path exists" : "Path not found"
Earlier versions: PowerShell gives back values that haven't been assigned.
$a = if ($condition) { $true } else { $false }
Example:
"The item is $(if ($price -gt 100) { 'expensive' } else { 'cheap' })"
Let's try it out:
$price = 150
The item is expensive
$price = 75
The item is cheap
'The condition is {0}.' -f ('false','true')[$condition]
You can use the PowerShell’s native way:
"The condition is " + (&{If($Condition) {"True"} Else {"False"}}) + "."
But as this adds a lot of parenthesis and brackets to your syntax, you might consider the following (probably one of the smallest existing) CmdLet:
Function IIf($If, $Right, $Wrong) {If ($If) {$Right} Else {$Wrong}}
Which will simplify your command to:
"The condition is " + (IIf $Condition "True" "False") + "."
Added 2014-09-19:
I have been using the IIf
cmdlet now for a while, and I still think it will make syntaxes more readable in a lot of cases, but as I agree with Jason’s note about the unwanted side effect that both possible values will be evaluated even obviously only one value is used, I have changed the IIf
cmdlet a bit:
Function IIf($If, $IfTrue, $IfFalse) {
If ($If) {If ($IfTrue -is "ScriptBlock") {&$IfTrue} Else {$IfTrue}}
Else {If ($IfFalse -is "ScriptBlock") {&$IfFalse} Else {$IfFalse}}
}
Now you might add a ScriptBlock (surrounded by {}
's) instead of an object which will not be evaluated if it is not required as shown in this example:
IIf $a {1/$a} NaN
Or placed inline:
"The multiplicative inverse of $a is $(IIf $a {1/$a} NaN)."
In case $a
has a value other than zero, the multiplicative inverse is returned; otherwise, it will return NaN
(where the {1/$a}
is not evaluated).
Another nice example where it will make a quiet ambiguous syntax a lot simpler (especially in case you want to place it inline) is where you want to run a method on an object which could potentially be $Null
.
The native ‘If
’ way to do this, would be something like this:
If ($Object) {$a = $Object.Method()} Else {$a = $null}
(Note that the Else
part is often required in e.g. loops where you will need to reset $a
.)
With the IIf
cmdlet it will look like this:
$a = IIf $Object {$Object.Method()}
(Note that if the $Object
is $Null
, $a
will automatically be set to $Null
if no $IfFalse
value is supplied.)
Added 2014-09-19:
Minor change to the IIf
cmdlet which now sets the current object ($_ or $PSItem):
Function IIf($If, $Then, $Else) {
If ($If -IsNot "Boolean") {$_ = $If}
If ($If) {If ($Then -is "ScriptBlock") {&$Then} Else {$Then}}
Else {If ($Else -is "ScriptBlock") {&$Else} Else {$Else}}
}
This means you can simplify a statement (the PowerShell way) with a method on an object that could potentially be $Null
.
The general syntax for this will now be $a = IIf $Object {$_.Method()}
. A more common example will look something like:
$VolatileEnvironment = Get-Item -ErrorAction SilentlyContinue "HKCU:\Volatile Environment"
$UserName = IIf $VolatileEnvironment {$_.GetValue("UserName")}
Note that the command $VolatileEnvironment.GetValue("UserName")
will normally result in an "You cannot call a method on a null-valued expression." error if the concerned registry (HKCU:\Volatile Environment
) doesn’t exist; where the command IIf $VolatileEnvironment {$_.GetValue("UserName")}
will just return $Null
.
If the $If
parameter is a condition (something like $Number -lt 5
) or forced to a condition (with the [Bool]
type), the IIf
cmdlet won't overrule the current object, e.g.:
$RegistryKeys | ForEach {
$UserName = IIf ($Number -lt 5) {$_.GetValue("UserName")}
}
Or:
$RegistryKeys | ForEach {
$UserName = IIf [Bool]$VolatileEnvironment {$_.OtherMethod()}
}
Added 2020-03-20:
Using the ternary operator syntax
PowerShell 7.0 introduced a new syntax using the ternary operator. It follows the C# ternary operator syntax:
The ternary operator behaves like the simplified
if-else
statement. The<condition>
expression is evaluated and the result is converted to a boolean to determine which branch should be evaluated next:The
<if-true>
expression is executed if the<condition>
expression is true The<if-false>
expression is executed if the<condition>
expression is false
Example:
"The multiplicative inverse of $a is $($a ? (& {1/$a}) : 'NaN')."
PowerShell doesn't have support for inline ifs. You'll have to create your own function (as another answer suggests), or combine if/else statements on a single line (as another answer also suggests).
From blog post DIY: Ternary operator:
Relevant code:
# —————————————————————————
# Name: Invoke-Ternary
# Alias: ?:
# Author: Karl Prosser
# Desc: Similar to the C# ? : operator e.g.
# _name = (value != null) ? String.Empty : value;
# Usage: 1..10 | ?: {$_ -gt 5} {“Greater than 5;$_} {“Not greater than 5”;$_}
# —————————————————————————
set-alias ?: Invoke-Ternary -Option AllScope -Description “PSCX filter alias”
filter Invoke-Ternary ([scriptblock]$decider, [scriptblock]$ifTrue, [scriptblock]$ifFalse)
{
if (&$decider) {
&$ifTrue
} else {
&$ifFalse
}
}
And then you can use that like this:
$total = ($quantity * $price ) * (?: {$quantity -le 10} {.9} {.75})
This is the closest variant I have seen so far.