2015-07-10 252 views
79

據我所知,PowerShell似乎沒有所謂的ternary operator的內置表達式。PowerShell中的三元運算符

例如,在C語言中,它支持三元運算符,我可以寫這樣的:

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

如果實際上並不存在在PowerShell中,這將是最好的方式(即容易閱讀和維護)來完成相同的結果?

+4

在https://github.com/nightroman/PowerShellTraps/tree/master/Basic/Missing-ternary-operator看看。如果這是你正在尋找的東西,我可以讓它成爲一個答案。 –

+0

這是*條件*運算符或三元* if *。它不是「三元運算符」,因爲它意味着一個運算符(* any *運算符),它有三個參數。 –

+4

@Damien_The_Unbeliever這在技術上是真實的,但它通常被稱爲三元運算符。_「由於該運算符通常是該語言中唯一存在的三元運算符,因此有時簡稱爲」三元運算符「。在某些語言中,該運算符被稱爲」條件運算符「。 (https://en.wikipedia.org/wiki/Ternary_operation) – mguassa

回答

27

最近Powershell的結構,我已經能夠拿出來模擬那就是:

. ({'condition is false'},{'condition is true'})[$condition] 
+3

'({true},{false})[!$ condition]'稍微好一些(可能):a)傳統b)'$ condition'不一定是0或1或$ false,$ true。運算符'!'將它轉換爲需要的編輯。即'$ condition'可以是,比如42:!42〜$ false〜0〜第一個表達式。 –

+0

不完全相同,@RomanKuzmin。 mjolinor的例子返回一個字符串。但是插入到表達式的示例中的相同字符串值將返回一個腳本塊。 :-( –

+4

)通過'{true}'和'{false}'我的意思是''和'',而不是腳本塊,對不起,不準確 –

10

由於指定值時,三元運營商通常使用,它應該返回一個值。這是可以工作的方式:

[email protected]("value if false","value if true")[[byte](condition)] 

笨,但工作。也可以使用這種構造將int快速轉換爲另一個值,只需添加數組元素並指定一個返回基於0的非負值的表達式即可。

+0

這將迫使對左右選項的急切評估:這與三元操作的適當性有很大不同 – user2864740

5

既然我已經使用過很多次,並沒有看到它列在這裏,我將添加我的作品:

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

最醜陋的所有!

kinda source

+0

這將迫使人們進行急切的評估左右選項:這與三元操作有很大不同 – user2864740

18

每本PowerShell blog post,您可以創建一個別名來定義?:操作:

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}) 
+0

這是一種比其他解決方案更可讀的方式! – Balmipour

+0

似乎沒有理由'決定者'是一個腳本塊。如果'?:'被評估過,則需要評估參數。沒有腳本塊,它會有類似的用法,例如。 '(?:($ quantity -le 10){.9} {.75})' – user2864740

16

我也找了一個更好的回答,雖然愛德華的帖子中的解決方案是「好的」,但我想出了一個更加自然的方案solution in this blog post

簡短而親切:

# --------------------------------------------------------------------------- 
# 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" 
+0

這非常棒。我只是不喜歡使用'='作爲別名,因爲'=='在C#和許多其他語言中用於相等性檢查。在C#中有一些經驗在powershellers中很常見,並且使得產生的代碼有點混亂。 ':'可能是一個更好的別名。將它與變量賦值'=:'結合使用可能會提醒某人Pascal中使用的賦值,但在VB.NET和C#中沒有任何直接等價的(我知道的)。 – nohwnd

+0

您可以將其設置爲任何你想要的別名。 ':'或'〜'或者其他什麼漂浮你的船。 :D 'set-alias:Invoke-Assignment -Option AllScope -Description「FearTheCowboy's Invoke-Assignment。」' #ternary '$ message =:($ age> 50)? 「老人」:「年輕人」 #null coalescing '$ message =:$ foo ?? 「foo是空的」' ' –

+0

我知道,我做了,我只是在評論爲什麼我會使用別名。 – nohwnd

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

一切是偶然的複雜性,因此是避免。

對於或作爲表達,而不僅僅是分配的使用,包裝在$(),從而:

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

在等號的右側工作,但不像您期望的三元運算符 - 這些失敗:*「a」+ If($ condition){「true」} Else {「false」} *和* 「a」+(If($ condition){「true」} Else {「false」})* 這有效(我還不確定爲什麼):*「a」+ $(If($ condition){ 「true」} else {「false」})* – Lamarth

+3

@Lamarth - 這是因爲'$()'包裝器強制將語句評估爲表達式,因此返回false值的真實值,就像三元組運營商將被期望。這是您在PowerShell AFAIK中最接近的。 – KeithS

+0

與其他「非函數」答案不同,這也會*確保只執行一個分支。 – user2864740

0

下面是一個替代的自定義功能的方法:

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](-2..2)) |??? 'match' : 'nomatch'(或簡稱:[bool](-2..2) |??? 'match' : 'nomatch'
0

PowerShell中目前沒有一個本地Inline If(或ternary If),但你可以考慮使用自定義的cmdlet:

IIf <condition><condition-is-true><condition-is-false>
參見: PowerShell Inline If (IIf)

+0

鏈接的帖子顯示瞭如何創建一個'IIf'函數。但是,沒有標準的PowerShell'IIf'函數/ cmdlet。 – user2864740

+0

@ user2864740,那又如何?這是否排除了答案作爲這個問題的可能答案:「*什麼是最好的方式(即易於閱讀和維護)來實現相同的結果?「*但是,除此之外,鏈接指的是IIf **問題**(發佈於2014年9月5日)與*非常相似(重複?)。鏈接問題中的其他答案也可以看出作爲附加價值(並且不這樣做,主要是因爲它們是重複的)。 – iRon

+0

有一點解釋很長,這就是爲什麼*裸鏈接*不鼓勵,因爲如果存在重複則不會關閉沒有額外的附加信息,考慮一個非常小的解釋會顯着提高這種「裸鏈接」答案的質量:「Powershell不支持直接的'三元'(^但看其他答案)。但是,可以編寫一個函數,例如(^使用這種方法,或者查看其他答案)..「,但這不是我的*作業要添加。可以修改/更新/等等 – user2864740

0

嘗試powershell的開關聲明作爲替代,特別是對於變量賦值 - 多行,但可讀。

例,

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

您可以使用:

$var = q(condition) ? condtion_is_true : condition_is_false