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


214

Як я знаю, PowerShell, схоже, не має вбудованого вираження для так званого потрійного оператора .

Наприклад, мовою С, яка підтримує потрійний оператор, я можу написати щось на зразок:

<condition> ? <condition-is-true> : <condition-is-false>;

Якщо цього в PowerShell насправді не існує, що було б найкращим способом (тобто легким для читання та підтримки) досягненням того ж результату?


5
Погляньте на github.com/nightroman/PowerShellTraps/tree/master/Basic/… . Якщо це те, що ви шукаєте, я можу зробити це відповіддю.
Роман Кузьмін

2
Це умовний оператор або термінал, якщо . Це не "потрійний оператор", оскільки все це означає, що оператор ( будь-який оператор) бере три аргументи.
Damien_The_Unbeliever

7
@Damien_The_Unbeliever Це технічно правда, але її часто називають потрійним оператором. "Оскільки цей оператор часто є єдиним існуючим в мові потрійним оператором, його іноді просто називають" термінальним оператором ". У деяких мовах цього оператора називають" умовним оператором ".
Термінальна

Visual basic не має справжнього потрійного оператора, але вважає IF та IFF функціонально еквівалентними.
Метт

@Matt Це неправильно. IIF Функція завжди буде оцінювати обидва операнда. IfЗаява не буде - см stackoverflow.com/questions/1220411 / ... (нові версії VB.NET додали трикомпонентну вираз: If (x, y, z))
user2864740

Відповіді:


319
$result = If ($condition) {"true"} Else {"false"}

Все інше є випадковою складністю і, таким чином, його слід уникати.

Для використання в вираженні або як не в призначенні, загортайте його $()таким чином:

write-host  $(If ($condition) {"true"} Else {"false"}) 

3
Це працює з правого боку рівного, але не зовсім так, як ви очікували, що потрійний оператор - це не вдасться: "a" + If ($ умова) {"true"} Else {"false"} і "a" + (Якщо ($ умова) {"true"} інше {"false"}) Це працює (я ще не впевнений, чому): "a" + $ (якщо ($ умова) {"true"} інше {"false "})
Ламарт

12
@Lamarth - Це працює, тому що $()обгортка змушує оцінювати вислів як вираз, таким чином повертаючи або справжнє значення помилкового значення, як і слід було б очікувати від потрійного оператора. Це найближче до PowerShell AFAIK.
KeithS

4
На відміну від деяких інших "нефункціональних" відповідей, це також забезпечить виконання лише однієї гілки.
користувач2864740

63

Найближча конструкція PowerShell, яку я зміг придумати, щоб наслідувати це:

@({'condition is false'},{'condition is true'})[$condition]

15
({true}, {false})[!$condition]трохи краще (можливо): а) традиційний порядок правдивих і помилкових частин; б) $conditionне повинно бути лише 0 або 1 або $ false, $ true. Оператор !перетворює його за потребою. Тобто $conditionможе бути, скажімо, 42:! 42 ~ $ false ~ 0 ~ перший вираз.
Роман Кузьмін

1
Не зовсім ідентичний, @RomanKuzmin. Приклад mjolinor повертає рядок. Але ті ж самі рядкові значення з його прикладу, включені до вашого виразу, повертають блок сценарію. :-(
Майкл Соренс

5
До {true}і {false}я маю в виду <true expression>і <false expression>, що не скріптові блоки. Вибачте за неточність.
Роман Кузьмін

1
Дякую за роз’яснення, @ RomanKuzmin - тепер я бачу цінність у вашій пропозиції.
Майкл Соренс

12
Це змусить нетерпляче оцінити лівий і правий варіанти: це сильно відрізняється від правильної потрійної операції.
користувач2864740


24

Відповідно до цієї публікації в блозі PowerShell , ви можете створити псевдонім для визначення ?:оператора:

set-alias ?: Invoke-Ternary -Option AllScope -Description "PSCX filter alias"
filter Invoke-Ternary ([scriptblock]$decider, [scriptblock]$ifTrue, [scriptblock]$ifFalse) 
{
   if (&$decider) { 
      &$ifTrue
   } else { 
      &$ifFalse 
   }
}

Використовуйте його так:

$total = ($quantity * $price ) * (?:  {$quantity -le 10} {.9} {.75})

Здається, не існує причини, що "вирішувати" є блоком сценаріїв. Якщо ?:коли-небудь буде оцінено, знадобиться аргументація оцінюється. Без блоку скриптів він би мав подібне використання, наприклад. (?: ($quantity -le 10) {.9} {.75})
користувач2864740

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

1
@VanceMcCorkle Суцільна точка. Користувальницька ласистість вартує її вартості, якщо корисна часто. Але якщо ситуація трапляється рідко, я б дотримувався виразу "якщо".
Едвард Брей

21

Я теж шукав кращу відповідь, і хоча рішення в посту Едварда "нормально", я придумав набагато більш природне рішення в цій публікації в блозі

Коротке і солодке:

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

Що дозволяє легко робити такі речі (більше прикладів у публікації блогу):

$message == ($age > 50) ? "Old Man" :"Young Dude" 

1
це досить приголомшливо. Мені просто не подобається використовувати =псевдонім, тому що ==він використовується у C # та багатьох інших мовах для перевірки рівності. Наявність певного досвіду роботи на C # є досить поширеним явищем серед контролерів влади, і це робить код, що виникає, трохи заплутаним. :можливо, був би кращим псевдонімом. Використання його в поєднанні зі змінним призначенням =:може нагадувати комусь про призначення, яке використовується в Pascal, але не має жодного негайного еквівалента (що я знаю) у VB.NET, ні C #.
nohwnd

Ви можете встановити це на будь-який псевдонім, який хочете. : або що- ~небудь пливе ваш човен. : D set-alias : Invoke-Assignment -Option AllScope -Description "FearTheCowboy's Invoke-Assignment." # ternary $message =: ($age > 50) ? "Old Man" :"Young Dude" # null coalescing $message =: $foo ?? "foo was empty"`
Гаррет Серак

Я знаю, і я це робив, я просто коментував, чому я буду використовувати інший псевдонім.
nohwnd

15

Спробуйте оператор перемикання powershell як альтернативу, особливо для призначення змінних - кілька рядків, але читабельний.

Приклад,

$WinVer = switch ( Test-Path $Env:windir\SysWOW64 ) {
  $true    { "64-bit" }
  $false   { "32-bit" }
}
"This version of Windows is $WinVer"

1
Це може бути дещо більш багатослівним, але є багато переваг над багатьма іншими відповідями. Зокрема, немає ніяких залежностей ні від зовнішнього коду, ні від функцій, що копіюються / вставляються в сценарій або модуль.
бшаклетт

9

Оскільки при призначенні значення зазвичай використовується потрійний оператор, він повинен повернути значення. Це спосіб, який може працювати:

$var=@("value if false","value if true")[[byte](condition)]

Дурний, але робочий. Також ця конструкція може бути використана для швидкого перетворення int в інше значення, просто додайте елементи масиву та вкажіть вираз, який повертає 0-негативні значення на основі 0.


6
Це змусить нетерпляче оцінити лівий і правий варіанти: це сильно відрізняється від правильної потрійної операції.
користувач2864740

6

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

$var = @{$true="this is true";$false="this is false"}[1 -eq 1]

найгірший з усіх!

своєрідне джерело


4
Це змусить нетерпляче оцінити лівий і правий варіанти: це сильно відрізняється від правильної потрійної операції.
користувач2864740

@ user2864740 це тому, що в PS немає належної потрійної роботи. Це синтетичний варіант.
Viggo

2
@ ViggoLundén Відсутність належного потрійного оператора не зменшує правильність коментаря. Цей "синтетичний варіант" має різну поведінку .
користувач2864740

Ви маєте рацію, і після перечитування вашого коментаря я розумію, як це може по-справжньому змінитися. Дякую.
сода верба

5

Нещодавно я вдосконалив (відкрити PullRequest) потрійні умовні та зв'язані з нулем оператори в PoweShell lib 'Pscx'
Pls шукають мого рішення.


Моя тематична галузь github: UtilityModule_Invoke-Operators

Функції:

Invoke-Ternary
Invoke-TernaryAsPipe
Invoke-NullCoalescing
NullCoalescingAsPipe

Псевдоніми

Set-Alias :?:   Pscx\Invoke-Ternary                     -Description "PSCX alias"
Set-Alias ?:    Pscx\Invoke-TernaryAsPipe               -Description "PSCX alias"
Set-Alias :??   Pscx\Invoke-NullCoalescing              -Description "PSCX alias"
Set-Alias ??    Pscx\Invoke-NullCoalescingAsPipe        -Description "PSCX alias"

Використання

<condition_expression> |?: <true_expression> <false_expression>

<variable_expression> |?? <alternate_expression>

Як вираз ви можете передати:
$ null, літерал, змінну, "зовнішній" вираз ($ b-екв. 4) або сценарій блоку {$ b -eq 4}

Якщо змінна у виразі змінної $ null або не існує , альтернативний вираз оцінюється як вихідний.


4

Наразі PowerShell не має вбудованого Inline If (або потрійного If ), але ви можете розглянути можливість використання спеціального командлета:

IIf <condition> <condition-is-true> <condition-is-false>
Дивіться: PowerShell Inline If (IIf)


Пов'язані поштові показує , як створити в IIfфункцію. Однак не існує стандартної IIfфункції PowerShell / cmdlet.
користувач2864740

1
@ user2864740, то що? чи виключає це відповідь як можливу корисну відповідь на запитання: " що було б найкращим способом (тобто простим для читання та підтримки) для досягнення того ж результату?" Але це вбік, посилання стосується питання IIf (розміщеного 5 вересня 14 року), який дуже подібний до цього (дублікат?). Решта відповідей у ​​пов'язаному запитанні також можна розглядати як додану вартість (а де ні, це головним чином тому, що вони є дублікатами).
iRon

Трохи пояснень йде дуже довгий шлях, і саме тому голі посилання відбиваються, як це не закривається для дублікатів, якщо немає додаткової додаткової інформації. Вважайте, що дуже невелике пояснення значно підвищить якість такої голої відповіді на посилання : "Powershell не підтримує прямий" трійковий "(^ але див. Інші відповіді). Однак можна записати таку функцію, як (^ використовуючи цей метод, або дивіться інші відповіді) .. ", але це не моя робота додати. Відповіді можна змінити / оновити / тощо.
користувач2864740

@ user2864740, дякую за ваш відгук, я внесла відповідні корективи у відповідь.
iRon

1

Ось альтернативний підхід до спеціальної функції:

function Test-TernaryOperatorCondition {
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true, Mandatory = $true)]
        [bool]$ConditionResult
        ,
        [Parameter(Mandatory = $true, Position = 0)]
        [PSObject]$ValueIfTrue
        ,
        [Parameter(Mandatory = $true, Position = 1)]
        [ValidateSet(':')]
        [char]$Colon
        ,
        [Parameter(Mandatory = $true, Position = 2)]
        [PSObject]$ValueIfFalse
    )
    process {
        if ($ConditionResult) {
            $ValueIfTrue
        }
        else {
            $ValueIfFalse
        }
    }
}
set-alias -Name '???' -Value 'Test-TernaryOperatorCondition'

Приклад

1 -eq 1 |??? 'match' : 'nomatch'
1 -eq 2 |??? 'match' : 'nomatch'

Відмінні пояснення

  • Чому це 3 знаки питання замість 1?
    • ?Персонаж вже псевдонім Where-Object.
    • ?? використовується іншими мовами як нульовий оператор з’єднання, і я хотів уникнути плутанини.
  • Навіщо нам потрібна труба перед командою?
    • Оскільки я використовую конвеєр, щоб оцінити це, нам все одно потрібен цей символ, щоб передати стан у нашу функцію
  • Що станеться, якщо я передаю масив?
    • Ми отримуємо результат для кожного значення; тобто -2..2 |??? 'match' : 'nomatch'дає: match, match, nomatch, match, match(тобто, оскільки будь-який ненульовий int оцінює до true; в той час як нуль оцінює до false).
    • Якщо ви цього не хочете, перетворіть масив у bool; ([bool](-2..2)) |??? 'match' : 'nomatch'(Або просто: [bool](-2..2) |??? 'match' : 'nomatch')

1

Якщо ви просто шукаєте синтаксично простий спосіб призначити / повернути рядок чи число на основі булевої умови, ви можете скористатися оператором множення таким чином:

"Condition is "+("true"*$condition)+("false"*!$condition)
(12.34*$condition)+(56.78*!$condition)

Якщо вас цікавить результат лише тоді, коли щось є правдою, ви можете просто опустити помилкову частину (або навпаки), наприклад, просту систему балів:

$isTall = $true
$isDark = $false
$isHandsome = $true

$score = (2*$isTall)+(4*$isDark)+(10*$isHandsome)
"Score = $score"
# or
# "Score = $((2*$isTall)+(4*$isDark)+(10*$isHandsome))"

Зауважте, що булеве значення не повинно бути провідним членом у множенні, тобто $ умова * "справжній" тощо не працюватиме.


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