Starfall Projects
Starfall Projects

Reputation: 415

Elegant way of setting default value for variable in Powershell 6.0?

I have the following, which works but looks clunky:

if($config.contentDir){ 
    $contentDir = $config.contentDir
} else { 
    $contentDir = "contents"
}

Is there a nicer way of doing this? I have seen this answer here, but it isn't exactly "nicer". Just wondering if 6.0 brought any improvements?

I'm likely to be handling a large amount of config options, so it's going to get fairly messy.

Upvotes: 3

Views: 1922

Answers (4)

mklement0
mklement0

Reputation: 437100

Update:


PowerShell v6- solutions:

What you'e looking for is null-coalescing, which PowerShell doesn't have as of v7.0.0-preview.4.

For now, this will have to do:

$contentDir = if ($null -eq $config.contentDir) { 'content' } else { $config.contentDir }

Note: $null is deliberately placed on the LHS of -eq to unambiguously test for $null, because as the RHS it would act as a filter if the value to test happens to be array-valued.

An adaptation of Lee Daily's array-based answer enables a more concise solution:

$contentDir = ($config.ContentDir, 'content')[$null -eq $config.ContentDir]

Use of the ternary operator (conditional), which will be implemented in v7.0, enables a similarly concise equivalent:

$contentDir = $null -eq $config.contentDir ? 'content' : $config.contentDir

However, all these approaches have the following undesirable aspects:

  • They require an explicit reference to $null; note that if ($config.ContentDir) - i.e. coercing the value to a Boolean - may work with strings, but is not generally robust, because non-$null values such as 0 can evaluate to $false too.

  • $config.contentDir, the value to test for $null, must be accessed twice, which can have side effects.


Defining a custom function named, say, ??, can address these problems:

# Custom function that emulates null-coalescing.
function ?? ($PossiblyNull, $ValueIfNull) { 
  if ($null -eq $PossiblyNull) { $ValueIfNull } else { $PossiblyNull }
}

$contentDir = ?? $config.contentDir 'content'

However, such a custom function has down-sides:

The down-sides of custom functions are:

  • You need to include or import them into in every piece of code you want to use them in.

  • If you choose familiar name such as ??, the placement of operands can get confusing, because you must (invariably) place them differently in PowerShell, given the implementation as a function (e.g., a ?? b in C# vs. ?? $a $b in PowerShell) - especially once true null-coalescing gets implemented in PowerShell: see next section.

  • And, of course, calling a function adds overhead.


If this GitHub feature request is implemented, you'll be able to use true null-coalescing, which is both the most concise solution and avoids the aforementioned undesirable aspects:

# Hopefully soon
$contentDir = $config.contentDir ?? 'content'

A related feature also proposed in the linked GitHub issue is null-conditional assignment, $config.ContentDir ?= 'content'

Upvotes: 3

Bill_Stewart
Bill_Stewart

Reputation: 24525

This is a little shorter...

$contentDir = if ( $config.contentDir ) { $config.contentDir } else { "contents" }

You could also define an iif function:

function iif {
  param(
    [ScriptBlock] $testExpr,
    [ScriptBlock] $trueExpr,
    [ScriptBlock] $falseExpr
  )
  if ( & $testExpr ) {
    & $trueExpr
  }
  else {
    & $falseExpr
  }
}

Then you could shorten to this:

$contentDir = iif { $config.contentDir } { $config.contentDir } { "contents" }

As an aside, it looks like the next version of PowerShell will support the ternary operator (see https://devblogs.microsoft.com/powershell/powershell-7-preview-4/), so in the future, you'll be able to write something like:

$contentDir = $config.contentDir ? $config.contentDir : "contents"

Upvotes: 4

js2010
js2010

Reputation: 27428

Sort of like '||' in bash. If the first one is false or null, it will do the second one.

[void](($contentDir = $config.contentDir) -or ($contentDir = "contents"))

Upvotes: 0

Lee_Dailey
Lee_Dailey

Reputation: 7479

as Bill_Stewart showed, there is a ternary operator due in ps7. however, you can get something similar by using a two-item array and taking advantage of how PoSh will coerce values -- $False gives 0, $True gives 1.

$Config = [PSCustomObject]@{
    ContentDir = 'SomewhereElse'
    }
#$Config.ContentDir = ''

$ContentDir = @('contents', $Config.ContentDir)[[bool]$Config.ContentDir]

$ContentDir     

output with line 4 commented out = SomewhereElse
output with line 4 enabled = contents

Upvotes: 2

Related Questions