PowerShell'de üçlü operatör


214

Bildiğim kadarıyla, PowerShell üçlü operatör için yerleşik bir ifadeye sahip görünmüyor .

Örneğin, üçlü operatörü destekleyen C dilinde, şöyle bir şey yazabilirim:

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

Bu PowerShell'de gerçekten mevcut değilse, aynı sonucu elde etmenin en iyi yolu (yani okunması ve bakımı kolay) ne olurdu?


5
Github.com/nightroman/PowerShellTraps/tree/master/Basic/… adresine bir göz atın . Eğer aradığın buysa, cevap verebilirim.
Roman Kuzmin

2
Bu bir var koşullu operatör veya üçlü eğer . Bunun anlamı "üçlü operatör" değildir, çünkü tüm bunlar üç argüman alan bir operatördür ( herhangi bir operatör).
Damien_The_Unbeliever

7
@Damien_The_Unbeliever Bu teknik olarak doğrudur, ancak genellikle üçlü operatör olarak adlandırılır. "Bu operatör genellikle dilde mevcut tek üçlü operatör olduğundan, bazen" üçlü operatör "olarak adlandırılır. Bazı dillerde, bu operatör" koşullu operatör "olarak adlandırılır. Üçlü işlem
mguassa

Visual basic gerçek bir üçlü işleç içermez, ancak IF ve IFF'nin işlevsel olarak eşit olduğunu düşünür.
Matt

@Matt Bu yanlış. IIF Fonksiyon daima her iki işleci edecektir. Ifİfadesi olmayacak - bkz stackoverflow.com/questions/1220411/... : (daha yeni VB.NET sürümleri üçlü ifadesi eklendi If (x, y, z))
user2864740

Yanıtlar:


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

Diğer her şey tesadüfi karmaşıklıktır ve bu nedenle kaçınılmalıdır.

Yalnızca bir ödevde değil, bir ifadede veya bir ifade olarak kullanım için, ödevi sarın $(), böylece:

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

3
Bu eşittir sağ tarafında çalışır, ancak üçlü bir operatör beklediğiniz gibi değil - bunlar başarısız olur: "a" + If ($ condition) {"true"} Else {"false"} ve "a" + (Eğer ($ condition) {"true"} Başka {"false"}) Bu işe yarar (henüz neden olduğundan emin değilim): "a" + $ (($ condition) {"true"} Başka {"false "})
Lamarth

12
@Lamarth - Sarıcı $()ifadenin bir ifade olarak değerlendirilmesini zorladığı için çalışır , böylece üçlü bir operatörün beklediği gibi yanlış değerin gerçek değerini döndürür. Bu PowerShell AFAIK size en yakın olacak.
KeithS

4
Diğer "non işlev" bazı yanıtlar farklı olarak, bu olacak da tek dal yürütülür emin olun.
user2864740

63

Öykünmek için geldim en yakın PowerShell yapısı:

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

15
({true}, {false})[!$condition]biraz daha iyidir (belki de): a) doğru ve yanlış parçaların geleneksel düzeni; b) $conditionsadece 0 veya 1 veya $ false olmak zorunda değildir, $ true. Operatör !gerektiğinde dönüştürür. $conditionÖrneğin, 42 :! 42 ~ $ false ~ 0 ~ ilk ifade olabilir.
Roman Kuzmin

1
Tam olarak aynı değil, @RomanKuzmin. mjolinor örneği bir dize döndürür. Ancak, ifadenize eklenmiş örneğindeki aynı dize değerleri bir komut dosyası bloğu döndürür. :-(
Michael Sorens

5
By {true}ve {false}demek <true expression>ve <false expression>değil komut dosyası blokları. Doğru olmadığım için üzgünüm.
Roman Kuzmin

1
Açıklamanız için teşekkürler, @ RomanKuzmin - şimdi önerinizdeki değeri görüyorum.
Michael Sorens

12
Bu, sol ve sağ seçeneklerin istekli değerlendirmesini zorlayacaktır: bu, uygun bir üçlü operasyondan çok farklıdır.
user2864740


24

Bu PowerShell blog yayını başına bir ?:operatör tanımlamak için bir takma ad oluşturabilirsiniz :

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

Şöyle kullanın:

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

'Decider'ın bir script bloğu olması için bir neden yok gibi görünmüyor. Eğer ?:hiç değerlendirilirse, argümanların değerlendirilmesini gerektirecektir. Bir komut dosyası bloğu olmasaydı, benzer bir kullanımı olurdu, örneğin. (?: ($quantity -le 10) {.9} {.75})
user2864740

Bu harika bir özelliktir, ancak komut dosyanızı standart olarak oluşturabilir, örn. Diğer ad tanımı da onunla birlikte taşınmadıkça komut dosyanız veya bölümleri bağlantı vermeyebilir. Bu noktada temel olarak kendi alt dilinizi oluşturuyorsunuz. Tüm bunlar, karmaşıklığın artması ve okunabilirliğin azalması nedeniyle bakım maliyetinin artmasına neden olabilir.
Vance McCorkle

1
@VanceMcCorkle Katı nokta. Özel terslik, yararlı olması durumunda maliyetine değer. Ancak durum nadiren gerçekleşirse, bir "if" ifadesi ile devam ederdim.
Edward Brey

21

Ben de, daha iyi bir cevap aradım ve Edward'ın gönderisindeki çözüm "tamam" olsa da, bu blog yazısında çok daha doğal bir çözüm buldum

Kısa ve güzel:

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

Bu gibi şeyler yapmayı kolaylaştırır (blog yazısında daha fazla örnek):

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

1
bu oldukça harika. Sadece =takma ad olarak kullanmaktan hoşlanmıyorum , çünkü ==C # ve eşitlik kontrolü için diğer birçok dilde kullanılıyor. C # 'da biraz deneyime sahip olmak, powershellers arasında oldukça yaygındır ve bu da ortaya çıkan kodu biraz kafa karıştırıcı yapar. :muhtemelen daha iyi bir takma ad olurdu. Değişken atamayla birlikte =:kullanılması, Pascal'da kullanılan atamayı hatırlatabilir, ancak VB.NET veya C # 'da hemen eşdeğeri (bildiğim) yoktur.
nohwnd

Bunu istediğiniz diğer adlara ayarlayabilirsiniz. : ya ~da ya da teknenizde yüzen ne varsa : D set-alias : Invoke-Assignment -Option AllScope -Description "FearTheCowboy's Invoke-Assignment." # üçlü $message =: ($age > 50) ? "Old Man" :"Young Dude" # boş birleştirme $message =: $foo ?? "foo was empty"`
Garrett Serack

Biliyorum ve yaptım, neden başka bir takma isim kullanacağımı yorumluyordum.
nohwnd

15

Powershell'in anahtar deyimini, özellikle değişken atama için alternatif olarak deneyin - birden çok satır, ancak okunabilir.

Misal,

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

1
Bu biraz daha ayrıntılı olabilir, ancak diğer cevapların çoğuna göre önemli faydalar vardır. Özellikle, harici koda veya bir komut dosyasına veya modüle kopyalanan / yapıştırılan işlevlere bağımlılık yoktur.
bshacklett

9

Üçlü işleç genellikle değer atarken kullanıldığından, bir değer döndürmelidir. Bu şekilde çalışabilir:

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

Aptalca, ama çalışıyor. Ayrıca bu yapı, bir int'i hızlı bir şekilde başka bir değere dönüştürmek için kullanılabilir, sadece dizi öğeleri ekleyin ve 0 tabanlı negatif olmayan değerler döndüren bir ifade belirtin.


6
Bu, sol ve sağ seçeneklerin istekli değerlendirmesini zorlayacaktır: bu, uygun bir üçlü operasyondan çok farklıdır.
user2864740

6

Bunu zaten birçok kez kullandığım ve burada listelendiğini görmediğim için, parçamı ekleyeceğim:

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

en çirkin!

biraz kaynak


4
Bu, sol ve sağ seçeneklerin istekli değerlendirmesini zorlayacaktır: bu, uygun bir üçlü operasyondan çok farklıdır.
user2864740

@ user2864740, çünkü PS'de uygun üçlü işlem yoktur. Bu sentetik bir varyanttır.
Viggo Lundén

2
@ ViggoLundén Uygun üçlü bir operatörün olmaması, yorumun doğruluğunu azaltmaz. Bu "sentetik varyant" farklı davranışa sahiptir .
user2864740

Haklısınız ve yorumunuzu tekrar okuduktan sonra bunun nasıl gerçek bir fark yaratabileceğini anlıyorum. Teşekkürler.
sodawillow

5

Son zamanlarda PoweShell lib 'Pscx' Pls'deki üçlü koşullu ve boş birleştirici operatörleri geliştirdim (açık PullRequest)
benim çözümüm için bir göz atın.


Github konu dalı: UtilityModule_Invoke-Operators

Fonksiyonlar:

Invoke-Ternary
Invoke-TernaryAsPipe
Invoke-NullCoalescing
NullCoalescingAsPipe

Takma adlar

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"

kullanım

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

<variable_expression> |?? <alternate_expression>

İfade olarak şunları iletebilirsiniz:
$ null, değişmez değer, değişken, 'dış' ifade ($ b -eq 4) veya scriptblock {$ b -eq 4}

Değişken ifadesindeki bir değişken $ null veya mevcut değilse , alternatif ifade çıktı olarak değerlendirilir.


4

PowerShell şu anda yerel bir Satır İçi If (veya üçlü If ) içermiyor, ancak özel cmdlet'i kullanmayı düşünebilirsiniz:

IIf <condition> <condition-is-true> <condition-is-false>
Bkz. PowerShell Satır İçi (IIf)


Bağlı yazı, bir fonksiyonun nasıl oluşturulacağını gösterir IIf. Ancak standart PowerShell IIfişlevi / cmdlet'i yoktur.
user2864740

1
@ user2864740, Ne olmuş yani? bu, yanıtı " aynı sonucu elde etmenin en iyi yolu (yani okunması ve bakımı kolay) ne olabilir? Ancak bu bir yana, bağlantı buna çok benzeyen (yinelenen?) IIf sorusuna (5 Eylül 14'te yayınlanmıştır) atıfta bulunmaktadır . Bağlantılı sorudaki cevapların geri kalanı da katma değer olarak görülebilir (ve yok, esas olarak kopya oldukları için).
iRon

Biraz açıklama uzun bir yol kat ediyor ve bu nedenle , ek bilgi yoksa kopyalar için kapanmadığı için çıplak bağlantılar önerilmez. Çok küçük bir açıklamanın böyle çıplak bir bağlantı cevabının kalitesini önemli ölçüde artıracağını düşünün : "Powershell doğrudan bir 'üçlü' desteklemiyor (^ ancak diğer cevaplara bakınız). Ancak, (^ using Bu yöntem, ya da "..) diğer yanıtları görmek, ancak bu değil benim eklemek için bir iş. Cevaplar uygun olduğu şekilde değiştirilebilir / güncellenebilir / vb.
user2864740

@ user2864740, geri bildiriminiz için teşekkür ederiz, buna göre cevaba ayarlamalar yaptım.
iRon

1

İşte alternatif bir özel işlev yaklaşımı:

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'

Misal

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

Açıklanan Farklılıklar

  • Neden 1 yerine 3 soru işareti var?
    • ?Karakter zaten bir diğer adıdır Where-Object.
    • ?? diğer dillerde null birleştirme operatörü olarak kullanılır ve karışıklığı önlemek istedim.
  • Komuttan önce neden boruya ihtiyacımız var?
    • Bunu değerlendirmek için boru hattını kullandığımdan, koşulu işlevimize bağlamak için hala bu karaktere ihtiyacımız var
  • Bir diziye geçersem ne olur?
    • Her değer için bir sonuç alırız; yani şunu -2..2 |??? 'match' : 'nomatch'verir: match, match, nomatch, match, match(yani sıfır olmayan herhangi bir int değeri olarak değerlendirilir true; sıfır olarak değerlendirilirken false).
    • Bunu istemiyorsanız diziyi bir boole dönüştürün; ([bool](-2..2)) |??? 'match' : 'nomatch'(veya basitçe: [bool](-2..2) |??? 'match' : 'nomatch')

1

Yalnızca bir boole koşuluna bağlı olarak bir dize veya sayısal atamak / döndürmek için sözdizimsel olarak basit bir yol arıyorsanız, çarpma işlecini şu şekilde kullanabilirsiniz:

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

Eğer sadece bir şey doğruysa sonuçla ilgileniyorsanız, yanlış kısmı tamamen (ya da tam tersini) atlayabilirsiniz, örneğin basit bir puanlama sistemi:

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

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

Boole değerinin çarpmada baştaki terim olmaması gerektiğini unutmayın, yani $ condition * "true" vb. Çalışmaz.


1

PowerShell sürüm 7'den itibaren üçlü operatör PowerShell'de yerleşiktir.

1 -gt 2 ? "Yes" : "No"
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.