Нульове злиття в оболонці


115

Чи є нульовий оператор згортання в powerhell?

Я хотів би мати змогу виконувати ці команди c # в командних оболонках:

var s = myval ?? "new value";
var x = myval == null ? "" : otherval;

Відповіді:


133

Powershell 7+

Powershell 7 вводить в "Powershell" нативну нульову злиття, нульове умовне призначення та потрійні оператори.

Null Coalescing

$null ?? 100    # Result is 100

"Evaluated" ?? (Expensive-Operation "Not Evaluated")    # Right side here is not evaluated

Нульове умовне призначення

$x = $null
$x ??= 100    # $x is now 100
$x ??= 200    # $x remains 100

Термінальний оператор

$true  ? "this value returned" : "this expression not evaluated"
$false ? "this expression not evaluated" : "this value returned"

Попередні версії:

Немає потреби в розширеннях Powershell Community, ви можете використовувати стандартний Powershell, якщо заяви як вираз:

variable = if (condition) { expr1 } else { expr2 }

Отже, до замін для першого C # вираження:

var s = myval ?? "new value";

стає одним із наступних (залежно від уподобань):

$s = if ($myval -eq $null) { "new value" } else { $myval }
$s = if ($myval -ne $null) { $myval } else { "new value" }

або залежно від того, що може містити $ myval:

$s = if ($myval) { $myval } else { "new value" }

і другий C # вираз відображається аналогічно:

var x = myval == null ? "" : otherval;

стає

$x = if ($myval -eq $null) { "" } else { $otherval }

Тепер, якщо чесно, вони не дуже спритні, і ніде не зручні у використанні, як форми C #.

Ви також можете розглянути можливість його перетворення в дуже просту функцію, щоб зробити речі більш читаними:

function Coalesce($a, $b) { if ($a -ne $null) { $a } else { $b } }

$s = Coalesce $myval "new value"

або, можливо, як IfNull:

function IfNull($a, $b, $c) { if ($a -eq $null) { $b } else { $c } }

$s = IfNull $myval "new value" $myval
$x = IfNull $myval "" $otherval

Як ви бачите, дуже проста функція може дати вам трохи свободи синтаксису.

ОНОВЛЕННЯ: Один додатковий варіант, який слід врахувати в суміші, - це більш загальна функція IsTrue:

function IfTrue($a, $b, $c) { if ($a) { $b } else { $c } }

$x = IfTrue ($myval -eq $null) "" $otherval

Потім поєднайте, що є здатністю Powershell оголошувати псевдоніми, схожі на операторів, ви закінчуєте:

New-Alias "??" Coalesce

$s = ?? $myval "new value"

New-Alias "?:" IfTrue

$ans = ?: ($q -eq "meaning of life") 42 $otherval

Ясна річ, це не буде до смаку кожному, але, можливо, те, що ви шукаєте.

Як зазначає Томас, одна інша тонка різниця між версією C # і вищезгаданим полягає в тому, що C # виконує коротке замикання аргументів, але версії Powershell, що включають функції / псевдоніми, завжди будуть оцінювати всі аргументи. Якщо це проблема, використовуйте ifформу вираження.


6
Єдиний справжній еквівалент оператору з’єднання - це використання оператора if; проблема полягає в тому, що будь-який інший підхід оцінює всі операнди замість короткого замикання. "?? $ myval SomeReallyExpenisveFunction ()" викличе функцію, навіть якщо $ myval не є нульовою. Я припускаю, що можна затримати оцінку за допомогою сценаріїв блоків, але пам’ятайте, що блоки блоків сценаріїв НЕ закриваються, і все починає виникати незграбно.
Томас С. Тріас

Не працює в суворому режимі - це кидає The variable '$myval' cannot be retrieved because it has not been set..
BrainSlugs83

1
@ BrainSlugs83 Помилка, яку ви бачите в суворому режимі, не пов’язана з представленими нульовими параметрами згортання. Це просто стандарт, що Powershell перевіряє, чи визначається змінна спочатку. Якщо встановити $myval = $nullперед тестом, помилка повинна усунутись.
StephenD

1
Попередження, химерність передумови, нуль завжди слід порівнювати (незграбно) ставити нуль першим, тобто зараз $null -ne $a не можу знайти гідного посилання, але це давня практика.
dudeNumber4

90

PowerShell 7 та новіших версій

PowerShell 7 представляє багато нових функцій і переходить з .NET Framework в .NET Core. Станом на середину 2020 року, він не повністю замінив застарілі версії PowerShell через залежність від .NET Core, але Microsoft заявила, що вони мають намір сімейство Core врешті-решт замінити спадщину сімейства Framework. До того часу, коли ви прочитаєте це, у вашій системі може бути попередньо встановлена ​​сумісна версія PowerShell; якщо ні, дивіться https://github.com/powershell/powershell .

Згідно з документацією , в PowerShell 7.0 підтримуються такі оператори:

  1. Нульовий зв'язок :??
  2. Присвоєння нулю :??=
  3. Трійця :... ? ... : ...

Вони працюють так, як ви очікували при нульовому поєднанні:

$x = $a ?? $b ?? $c ?? 'default value'
$y ??= 'default value'

З моменту введення потрійного оператора, тепер можливе наступне, хоча це зайве з огляду на додавання оператора зведення нуля:

$x = $a -eq $null ? $b : $a

Станом на 7.0 також доступні наступні, якщо включенаPSNullConditionalOperators додаткова функція , як пояснено в документах ( 1 , 2 ):

  1. Нульовий умовний доступ для членів: ?.
  2. Нульовий умовний доступ члена для масивів та інших: ?[]

У них є кілька застережень:

  1. Оскільки вони експериментальні, вони можуть змінюватися. Можливо, вони вже не вважатимуться експериментальними до того часу, коли ви прочитаєте це, і список застережень може змінитися.
  2. Змінні повинні бути додані, ${}якщо за ними слідує один з експериментальних операторів, оскільки знаки запитання дозволені у назвах змінних. Незрозуміло, чи буде це так, якщо / коли функції закінчать експериментальний статус (див. Випуск № 11379 ). Наприклад, ${x}?.Test()використовує нового оператора, але $x?.Test()працює Test()на змінній з назвою $x?.
  3. Немає жодного ?(оператора, як ви могли очікувати, якщо ви приїжджаєте з TypeScript. Наступне не працює:$x.Test?()

PowerShell 6 і новіші версії

Версії PowerShell до 7 мають дійсний оператор з’єднання нуля або, принаймні, оператора, здатного до такої поведінки. Цей оператор -ne:

# Format:
# ($a, $b, $c -ne $null)[0]
($null, 'alpha', 1 -ne $null)[0]

# Output:
alpha

Це трохи більш універсально, ніж оператор зведення нуля, оскільки він створює масив усіх ненульових елементів:

$items = $null, 'alpha', 5, 0, '', @(), $null, $true, $false
$instances = $items -ne $null
[string]::Join(', ', ($instances | ForEach-Object -Process { $_.GetType() }))

# Result:
System.String, System.Int32, System.Int32, System.String, System.Object[],
System.Boolean, System.Boolean

-eq працює аналогічно, що корисно для підрахунку нульових записів:

($null, 'a', $null -eq $null).Length

# Result:
2

Але все одно, ось типовий випадок дзеркального відображення ??оператора C # :

'Filename: {0}' -f ($filename, 'Unknown' -ne $null)[0] | Write-Output

Пояснення

Це пояснення базується на пропозиції щодо редагування анонімного користувача. Дякую, хто б ти не був!

Виходячи з порядку операцій, це працює в наступному порядку:

  1. ,Оператор створює масив значень для тестування.
  2. В -neоператорському відфільтровує будь-які елементи з масиву , які відповідають вказаним значенням - в даному випадку, нульовий. Результатом є масив ненульових значень у тому ж порядку, що і масив, створений на етапі 1.
  3. [0] використовується для вибору першого елемента відфільтрованого масиву.

Спрощення цього:

  1. Створіть масив можливих значень у бажаному порядку
  2. Виключіть усі масивні значення з масиву
  3. Візьміть перший елемент з отриманого масиву

Коваджі

На відміну від оператора з нульовим злиттям C #, кожне можливе вираження буде оцінено, оскільки перший крок - це створення масиву.


Я в кінцевому підсумку використовував модифіковану версію вашої відповіді в моєму , тому що мені потрібно було дозволити, щоб всі екземпляри в коалессе були нульовими, не кидаючи винятку. Дуже приємний спосіб зробити це, хоча, багато чого навчився. :)
Джонні Сковдал

Цей спосіб запобігає короткому замиканню. Визначте function DidItRun($a) { Write-Host "It ran with $a"; return $a }і запустіть, ((DidItRun $null), (DidItRun 'alpha'), 1 -ne $null)[0]щоб побачити це.
jpmc26

@ jpmc26 Так, це за дизайном.
Zenexer

Будь-яка спроба визначити Coalesce функцію також може усунути коротке замикання чи не так?
Кріс Ф Керролл

8
Ахххх пріоритет оператора PowerShell вбиває мене! Я завжди забуваю, що оператор з комами ,має такий високий пріоритет. Для тих, хто плутається в тому, як це працює, ($null, 'alpha', 1 -ne $null)[0]те саме, що (($null, 'alpha', 1) -ne $null)[0]. Насправді єдиними двома "тире" операторами з більш високим пріоритетом є -splitі -join(і одинарний тире? Тобто. -4Або -'56').
Плутон

15

Це лише половина відповіді на першу половину запитання, тож відповідь на чверть, якщо ви хочете, але є набагато простіша альтернатива оператору зсуву нуля за умови, що значення за замовчуванням, яке ви хочете використовувати, є фактично значенням за замовчуванням для типу :

string s = myval ?? "";

Можна написати в Powershell як:

([string]myval)

Або

int d = myval ?? 0;

перекладає на Powershell:

([int]myval)

Я знайшов перший з них корисним при обробці елемента XML, який може не існувати, і який, якщо він існує, міг би мати небажаний пробіл навколо нього:

$name = ([string]$row.td[0]).Trim()

Передача на рядок захищає від недійсності елемента та запобігає будь-якому ризику Trim()відмови.


([string]$null)-> ""здається "корисним, але невдалим побічним ефектом" :(
user2864740


9

Якщо ви встановите модуль розширень Powershell Community, ви можете використовувати:

?? є псевдонімом Invoke-NullCoalescing.

$s = ?? {$myval}  {"New Value"}

?: псевдонім для Invoke-Ternary.

$x = ?: {$myval -eq $null} {""} {$otherval}

Власне, це не команди PowerShell. Ви отримали їх разом з pscx:?: ->
Invoke-Ternary

... пропущений фактичний код та результат ..;) Get-Command -Module pscx -CommandType псевдонім | де {$ _. ім'я -матч '\ ?.' } | foreach {"{0}: {1}" -f $ _. Ім'я, $ _. Визначення}?:: Invoke-Ternary ?? : Invoke-NullCoalescing
BartekB

На жаль, ви абсолютно праві. Я часто забуваю, що у мене навіть є завантаження цього модуля.
EBGreen



2
function coalesce {
   Param ([string[]]$list)
   #$default = $list[-1]
   $coalesced = ($list -ne $null)
   $coalesced[0]
 }
 function coalesce_empty { #COALESCE for empty_strings

   Param ([string[]]$list)
   #$default = $list[-1]
   $coalesced = (($list -ne $null) -ne '')[0]
   $coalesced[0]
 }

2

Часто я виявляю, що мені також потрібно вважати порожню рядок як нульовою при використанні coalesce. Я закінчив написати для цього функцію, яка використовує рішення Zenexer для з’єднання для простого нульового зрощення, а потім використала Keith Hill для нульової чи порожньої перевірки, і додала, що як прапор, щоб моя функція могла виконувати і те, і інше.

Однією з переваг цієї функції є те, що вона також обробляє всі елементи нульовими (або порожніми), не кидаючи виняток. Його також можна використовувати для довільних безлічі вхідних змінних, завдяки тому, як PowerShell обробляє вхід масиву.

function Coalesce([string[]] $StringsToLookThrough, [switch]$EmptyStringAsNull) {
  if ($EmptyStringAsNull.IsPresent) {
    return ($StringsToLookThrough | Where-Object { $_ } | Select-Object -first 1)
  } else {
    return (($StringsToLookThrough -ne $null) | Select-Object -first 1)
  }  
}

Це дає такі результати тесту:

Null coallesce tests:
1 (w/o flag)  - empty/null/'end'                 : 
1 (with flag) - empty/null/'end'                 : end
2 (w/o flag)  - empty/null                       : 
2 (with flag) - empty/null                       : 
3 (w/o flag)  - empty/null/$false/'end'          : 
3 (with flag) - empty/null/$false/'end'          : False
4 (w/o flag)  - empty/null/"$false"/'end'        : 
4 (with flag) - empty/null/"$false"/'end'        : False
5 (w/o flag)  - empty/'false'/null/"$false"/'end': 
5 (with flag) - empty/'false'/null/"$false"/'end': false

Код тесту:

Write-Host "Null coalesce tests:"
Write-Host "1 (w/o flag)  - empty/null/'end'                 :" (Coalesce '', $null, 'end')
Write-Host "1 (with flag) - empty/null/'end'                 :" (Coalesce '', $null, 'end' -EmptyStringAsNull)
Write-Host "2 (w/o flag)  - empty/null                       :" (Coalesce('', $null))
Write-Host "2 (with flag) - empty/null                       :" (Coalesce('', $null) -EmptyStringAsNull)
Write-Host "3 (w/o flag)  - empty/null/`$false/'end'          :" (Coalesce '', $null, $false, 'end')
Write-Host "3 (with flag) - empty/null/`$false/'end'          :" (Coalesce '', $null, $false, 'end' -EmptyStringAsNull)
Write-Host "4 (w/o flag)  - empty/null/`"`$false`"/'end'        :" (Coalesce '', $null, "$false", 'end')
Write-Host "4 (with flag) - empty/null/`"`$false`"/'end'        :" (Coalesce '', $null, "$false", 'end' -EmptyStringAsNull)
Write-Host "5 (w/o flag)  - empty/'false'/null/`"`$false`"/'end':" (Coalesce '', 'false', $null, "$false", 'end')
Write-Host "5 (with flag) - empty/'false'/null/`"`$false`"/'end':" (Coalesce '', 'false', $null, "$false", 'end' -EmptyStringAsNull)

1

Найближче, що я можу отримати: $Val = $MyVal |?? "Default Value"

Я реалізував оператор null coalescing для описаного вище:

function NullCoalesc {
    param (
        [Parameter(ValueFromPipeline=$true)]$Value,
        [Parameter(Position=0)]$Default
    )

    if ($Value) { $Value } else { $Default }
}

Set-Alias -Name "??" -Value NullCoalesc

Умовна потрійна оператор може бути реалізована в Similary чином.

function ConditionalTernary {
    param (
        [Parameter(ValueFromPipeline=$true)]$Value,
        [Parameter(Position=0)]$First,
        [Parameter(Position=1)]$Second
    )

    if ($Value) { $First } else { $Second }
}

Set-Alias -Name "?:" -Value ConditionalTernary

І використовується як: $Val = $MyVal |?: $MyVal "Default Value"

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.