[powershell] Ternary operator in PowerShell

From what I know, PowerShell doesn't seem to have a built-in expression for the so-called ternary operator.

For example, in the C language, which supports the ternary operator, I could write something like:

<condition> ? <condition-is-true> : <condition-is-false>;

If that doesn't really exist in PowerShell, what would be the best way (i.e. easy to read and to maintain) to accomplish the same result?

This question is related to powershell ternary-operator conditional-operator

The answer is


Since I have used this many times already and didn't see it listed here, I'll add my piece :

$var = @{$true="this is true";$false="this is false"}[1 -eq 1]

ugliest of all !

kinda source


Per this PowerShell blog post, you can create an alias to define a ?: operator:

set-alias ?: Invoke-Ternary -Option AllScope -Description "PSCX filter alias"
filter Invoke-Ternary ([scriptblock]$decider, [scriptblock]$ifTrue, [scriptblock]$ifFalse) 
{
   if (&$decider) { 
      &$ifTrue
   } else { 
      &$ifFalse 
   }
}

Use it like this:

$total = ($quantity * $price ) * (?:  {$quantity -le 10} {.9} {.75})

Powershell 7 has it. https://toastit.dev/2019/09/25/ternary-operator-powershell-7/

PS C:\Users\js> 0 ? 'yes' : 'no'
no
PS C:\Users\js> 1 ? 'yes' : 'no'
yes

As of PowerShell version 7, the ternary operator is built into PowerShell.

1 -gt 2 ? "Yes" : "No"
# Returns "No"

1 -gt 2 ? 'Yes' : $null
# Get a $null response for false-y return value

Here's an alternative custom function approach:

function Test-TernaryOperatorCondition {
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true, Mandatory = $true)]
        [bool]$ConditionResult
        ,
        [Parameter(Mandatory = $true, Position = 0)]
        [PSObject]$ValueIfTrue
        ,
        [Parameter(Mandatory = $true, Position = 1)]
        [ValidateSet(':')]
        [char]$Colon
        ,
        [Parameter(Mandatory = $true, Position = 2)]
        [PSObject]$ValueIfFalse
    )
    process {
        if ($ConditionResult) {
            $ValueIfTrue
        }
        else {
            $ValueIfFalse
        }
    }
}
set-alias -Name '???' -Value 'Test-TernaryOperatorCondition'

Example

1 -eq 1 |??? 'match' : 'nomatch'
1 -eq 2 |??? 'match' : 'nomatch'

Differences Explained

  • Why is it 3 question marks instead of 1?
    • The ? character is already an alias for Where-Object.
    • ?? is used in other languages as a null coalescing operator, and I wanted to avoid confusion.
  • Why do we need the pipe before the command?
    • Since I'm utilising the pipeline to evaluate this, we still need this character to pipe the condition into our function
  • What happens if I pass in an array?
    • We get a result for each value; i.e. -2..2 |??? 'match' : 'nomatch' gives: match, match, nomatch, match, match (i.e. since any non-zero int evaluates to true; whilst zero evaluates to false).
    • If you don't want that, convert the array to a bool; ([bool](-2..2)) |??? 'match' : 'nomatch' (or simply: [bool](-2..2) |??? 'match' : 'nomatch')

I've recently improved (open PullRequest) the ternary conditional and null-coalescing operators in the PoweShell lib 'Pscx'
Pls have a look for my solution.


My github topic branch: UtilityModule_Invoke-Operators

Functions:

Invoke-Ternary
Invoke-TernaryAsPipe
Invoke-NullCoalescing
NullCoalescingAsPipe

Aliases

Set-Alias :?:   Pscx\Invoke-Ternary                     -Description "PSCX alias"
Set-Alias ?:    Pscx\Invoke-TernaryAsPipe               -Description "PSCX alias"
Set-Alias :??   Pscx\Invoke-NullCoalescing              -Description "PSCX alias"
Set-Alias ??    Pscx\Invoke-NullCoalescingAsPipe        -Description "PSCX alias"

Usage

<condition_expression> |?: <true_expression> <false_expression>

<variable_expression> |?? <alternate_expression>

As expression you can pass:
$null, a literal, a variable, an 'external' expression ($b -eq 4) or a scriptblock {$b -eq 4}

If a variable in the variable expression is $null or not existing, the alternate expression is evaluated as output.


PowerShell currently doesn't didn't have a native Inline If (or ternary If) but you could consider to use the custom cmdlet:

IIf <condition> <condition-is-true> <condition-is-false>

See: PowerShell inline If (IIf)


$result = If ($condition) {"true"} Else {"false"}

Everything else is incidental complexity and thus to be avoided.

For use in or as an expression, not just an assignment, wrap it in $(), thus:

write-host  $(If ($condition) {"true"} Else {"false"}) 

I too, looked for a better answer, and while the solution in Edward's post is "ok", I came up with a far more natural solution in this blog post

Short and sweet:

# ---------------------------------------------------------------------------
# Name:   Invoke-Assignment
# Alias:  =
# Author: Garrett Serack (@FearTheCowboy)
# Desc:   Enables expressions like the C# operators: 
#         Ternary: 
#             <condition> ? <trueresult> : <falseresult> 
#             e.g. 
#                status = (age > 50) ? "old" : "young";
#         Null-Coalescing 
#             <value> ?? <value-if-value-is-null>
#             e.g.
#                name = GetName() ?? "No Name";
#             
# Ternary Usage:  
#         $status == ($age > 50) ? "old" : "young"
#
# Null Coalescing Usage:
#         $name = (get-name) ? "No Name" 
# ---------------------------------------------------------------------------

# returns the evaluated value of the parameter passed in, 
# executing it, if it is a scriptblock   
function eval($item) {
    if( $item -ne $null ) {
        if( $item -is "ScriptBlock" ) {
            return & $item
        }
        return $item
    }
    return $null
}

# an extended assignment function; implements logic for Ternarys and Null-Coalescing expressions
function Invoke-Assignment {
    if( $args ) {
        # ternary
        if ($p = [array]::IndexOf($args,'?' )+1) {
            if (eval($args[0])) {
                return eval($args[$p])
            } 
            return eval($args[([array]::IndexOf($args,':',$p))+1]) 
        }

        # null-coalescing
        if ($p = ([array]::IndexOf($args,'??',$p)+1)) {
            if ($result = eval($args[0])) {
                return $result
            } 
            return eval($args[$p])
        } 

        # neither ternary or null-coalescing, just a value  
        return eval($args[0])
    }
    return $null
}

# alias the function to the equals sign (which doesn't impede the normal use of = )
set-alias = Invoke-Assignment -Option AllScope -Description "FearTheCowboy's Invoke-Assignment."

Which makes it easy to do stuff like (more examples in blog post):

$message == ($age > 50) ? "Old Man" :"Young Dude" 

Since a ternary operator is usually used when assigning value, it should return a value. This is the way that can work:

$var=@("value if false","value if true")[[byte](condition)]

Stupid, but working. Also this construction can be used to quickly turn an int into another value, just add array elements and specify an expression that returns 0-based non-negative values.


If you're just looking for a syntactically simple way to assign/return a string or numeric based on a boolean condition, you can use the multiplication operator like this:

"Condition is "+("true"*$condition)+("false"*!$condition)
(12.34*$condition)+(56.78*!$condition)

If you're only ever interested in the result when something is true, you can just omit the false part entirely (or vice versa), e.g. a simple scoring system:

$isTall = $true
$isDark = $false
$isHandsome = $true

$score = (2*$isTall)+(4*$isDark)+(10*$isHandsome)
"Score = $score"
# or
# "Score = $((2*$isTall)+(4*$isDark)+(10*$isHandsome))"

Note that the boolean value should not be the leading term in the multiplication, i.e. $condition*"true" etc. won't work.


The closest PowerShell construct I've been able to come up with to emulate that is:

@({'condition is false'},{'condition is true'})[$condition]

Try powershell's switch statement as an alternative, especially for variable assignment - multiple lines, but readable.

Example,

$WinVer = switch ( Test-Path $Env:windir\SysWOW64 ) {
  $true    { "64-bit" }
  $false   { "32-bit" }
}
"This version of Windows is $WinVer"

Examples related to powershell

Why powershell does not run Angular commands? How do I install the Nuget provider for PowerShell on a unconnected machine so I can install a nuget package from the PS command line? How to print environment variables to the console in PowerShell? Check if a string is not NULL or EMPTY The term 'ng' is not recognized as the name of a cmdlet VSCode Change Default Terminal 'Connect-MsolService' is not recognized as the name of a cmdlet Powershell Invoke-WebRequest Fails with SSL/TLS Secure Channel Install-Module : The term 'Install-Module' is not recognized as the name of a cmdlet Change directory in PowerShell

Examples related to ternary-operator

PHP ternary operator vs null coalescing operator Ternary operator in PowerShell What is the idiomatic Go equivalent of C's ternary operator? How to write a PHP ternary operator One-line list comprehension: if-else variants Angularjs if-then-else construction in expression Conditional statement in a one line lambda function in python? inline conditionals in angular.js Ternary operator in AngularJS templates Omitting the second expression when using the if-else shorthand

Examples related to conditional-operator

Ternary operator in PowerShell Javascript one line If...else...else if statement How to do one-liner if else statement? What is the idiomatic Go equivalent of C's ternary operator? bash "if [ false ];" returns true instead of false -- why? One-line list comprehension: if-else variants Kotlin Ternary Conditional Operator Conditional statement in a one line lambda function in python? ORACLE IIF Statement Twig ternary operator, Shorthand if-then-else