It took me a long time to actually start using PowerShell for my daily scripting tasks, mainly beacuse I was so damn good at CMD shell scripts, and it was such a hassle to learn to do everything differently.

However, as I worked more with PowerShell, I got to like it a lot, and now use it for virtually all my automation needs.

Daily dose of what’s wrong

A couple of big gripes come from the lack of a decent ternary operator in the language–which is a very terse way of cramming a whole if/else statement into a single expression:

A C# Example:

  // The ugly, bloated mess
string dude;
if( age > 50 ) {
dude = "Old Man"
} else {
dude = "Young Punk"
}

// Using a ternary, cleans up your code!
var dude = age > 50 ? "Old Man" : "Young Punk";

That time when someone tried to fix it with some duck-tape

Sadly, there exists no comparable feature in PowerShell. Searching the internets, I found an attempt to make something that is kinda the same:

From a blog post by Jeffrey Snover:

# ---------------------------------------------------------------------------
# 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
}
}

Which lets one use a construct that looks like this:

dude =  (?:  {$age -gt 50} {"Old Man"} {"Young Punk"})

sigh … I’ll give high marks for terse, but … not really the same readability as a C-style ternary.

Hack like nobody is watching

I have made (in my ever-so-humble opinion) a far smarter way to accomplish the support of a Ternary in PowerShell.

Let’s take a look at some examples, and I’ll show the code to accomplish this at the end.


Simple, straightforward ternary

$x == ( 'a' -eq 'a' ) ? "yes" : "no"
echo "Result: $x"
Result: yes

How about if it’s false

$x == ('a' -eq 'b' ) ? "that would be not correct" : "of course they are not equal"
echo "Result: $x"
Result: of course they are not equal

More fun

$x == ('a' -lt 'b' ) ? ('a' -ne 'b') : ('a' -eq 'b' )
echo "Result: $x"
Result: True

The other thing that’s missing from a PowerShell is a null-coalescing operator. In a c# example:

  // An ugly, bloated mess
var answer = SomeFunction();
if( answer == null ) {
answer = "not found";
}

// Using the null-coalescing operator:
var answer = SomeFunction() ?? "not found";

Which offers a clean, tight and simple way of saying if the answer is null, then use this answer instead. Maybe we can do the samething in PowerShell?

How much would you pay for a null-coalescing operator like C# ?

$z == $null ?? "This works!"
echo "Result: $z"
Result: This works!

Of course, it still thinks like powershell so 0, false and $null are all still ‘negative’

$b == (1 +2 -3) ?? 100
echo "Result: $b"
Result: 100

And regular numbers work nice:

$b == (1 +2 +3) ?? 100
echo "Result: $b"
Result : 6

Let’s try some more complicated examples

function get-null { return $null }

function get-somevalue { return "SomeValue" }

function invoke-sample {
# this is what I've wanted for years!
return = { get-null } ?? { get-somevalue }
}

echo "Result: $(invoke-sample)"
Result: SomeValue

A slight variation

function invoke-sample { 
# this is what I've wanted for years!
return = ( get-null ) ?? ( get-somevalue )
}

echo "Result: $(invoke-sample)"
Result: SomeValue

What does this one do?

function invoke-sample { 
# this is what I've wanted for years!
return = get-null ?? ( get-somevalue )
}

echo "Result: $(invoke-sample)"
# perhaps not so surpising...
get-null

We can drop the pretenses; you should have a clue by now.

= $null ?? { "still" + "right" } 
stillright

A couple more bits of fun:

echo (= 0 ? 100 : 200 )
echo (= 1 ? 100 : 200 )
200
100

Taking a peek behind the curtain

The ever-so-clever PowerShell enthusiasts will have probably guessed why this works.

It turns out that the power of PowerShell’s aliases is actually quite amazing, and when combined with a means of evaluating a parameter-regardless if it’s an scriptblock or just a value made it pretty simple to ‘extend’ assignment with an extra equal sign =

# ---------------------------------------------------------------------------
# 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."

Now, go forth, and bring terseness and compaction to your scripts!



blog comments powered by Disqus

Published

11 December 2015

Category

PowerShell

Tags