Элегантный способ установки значения по умолчанию для переменной в Powershell 6.0?

У меня есть следующее, которое работает, но выглядит неуклюже:

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

Есть ли способ сделать это лучше? Я видел здесь этот ответ, но он не совсем "лучше". Просто интересно, принесла ли 6.0 какие-либо улучшения?

Я, скорее всего, буду обрабатывать большое количество параметров конфигурации, так что это будет довольно беспорядочно.

4 ответа

Решение

Это немного короче...

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

Вы также можете определить iif функция:

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

Тогда вы могли бы сократить до этого:

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

Кроме того, похоже, что следующая версия PowerShell будет поддерживать тернарный оператор (см. https://devblogs.microsoft.com/powershell/powershell-7-preview-4/), поэтому в будущем вы будете может написать что-то вроде:

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

То, что вы ищете, - это объединение нуля, которого в PowerShell нет в версии v7.0.0-preview.4.

А пока для этого нужно будет:

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

Примечание: $null намеренно размещен на левой стороне -eq однозначно проверить $null, потому что как RHS он будет действовать как фильтр, если проверяемое значение будет иметь значение массива.

Адаптация ответа Ли Дейли на основе массива позволяет получить более лаконичное решение:

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

Использование тернарного оператора (условного), который будет реализован в версии 7.0, позволяет получить аналогичный краткий эквивалент:

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

Однако все эти подходы имеют следующие нежелательные аспекты:

  • Они требуют явной ссылки на $null; Обратите внимание, чтоif ($config.ContentDir)- т.е. приведение значения к логическому - может работать со строками, но обычно не является надежным, потому что не-$null такие ценности, как 0 может оценить $false тоже.

  • $config.contentDir, значение для проверки $null, необходимо получить доступ дважды, что может иметь побочные эффекты.


Определение пользовательской функции с именем, скажем,??, может решить эти проблемы:

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

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

Однако у такой пользовательской функции есть и минусы:

В понижающем стороны пользовательских функций являются:

  • Вам необходимо включить или импортировать их в каждый фрагмент кода, в котором вы хотите их использовать.

  • Если вы выберете знакомое имя, например ??, То размещение операндов может получить в заблуждение, потому что вы должны (всегда) разместить их по- разному в PowerShell, при реализации в качестве функции (например,a ?? b в C# vs. ?? $a $b в PowerShell) - особенно после того, как в PowerShell будет реализовано истинное слияние нуля: см. следующий раздел.

  • И, конечно же, вызов функции увеличивает накладные расходы.


Если этот запрос функции GitHub будет реализован, вы сможете использовать истинное объединение с нулевым значением, что является одновременно наиболее кратким решением и позволяет избежать вышеупомянутых нежелательных аспектов:

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

Связанная функция, также предложенная в связанной проблеме GitHub, - это условное присвоение null,$config.ContentDir ?= 'content'

Как Bill_Stewartпоказал, что в ps7 есть тернарный оператор. однако вы можете получить нечто подобное, используя массив из двух элементов и пользуясь тем, как PoSh будет приводить значения -$False дает 0, $True дает 1.

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

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

$ContentDir     

вывод с закомментированной строкой 4 = SomewhereElse
вывод с включенной строкой 4 = contents

Что-то вроде "||" в баше. Если первое значение false или null, оно выполнит второе.

[void](($contentDir = $config.contentDir) -or ($contentDir = "contents"))
Другие вопросы по тегам