简体   繁体   中英

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.

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"

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) - ie 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 (eg, 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'

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

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"))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM