ผู้ประกอบการที่สามใน PowerShell


214

จากสิ่งที่ฉันรู้ PowerShell ดูเหมือนจะไม่ได้มีการแสดงออกในตัวสำหรับผู้ประกอบการที่เรียกว่าประกอบไปด้วย

ตัวอย่างเช่นในภาษา C ซึ่งรองรับผู้ประกอบการที่ประกอบไปด้วยสามคนฉันสามารถเขียนสิ่งที่ชอบ:

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

หากสิ่งนั้นไม่มีอยู่จริงใน PowerShell สิ่งที่จะเป็นวิธีที่ดีที่สุด (เช่นง่ายต่อการอ่านและบำรุงรักษา) เพื่อให้ได้ผลลัพธ์เดียวกัน


5
ลองดูที่github.com/nightroman/PowerShellTraps/tree/master/Basic/... หากนี่คือสิ่งที่คุณกำลังมองหาฉันสามารถทำให้มันเป็นคำตอบ
Roman Kuzmin

2
มันเป็นผู้ประกอบการตามเงื่อนไขหรือประกอบไปด้วยถ้า ไม่ใช่ "ตัวดำเนินการที่สาม" เนื่องจากทั้งหมดนั้นหมายถึงตัวดำเนินการ ( ตัวดำเนินการใด ๆ ) ที่ใช้อาร์กิวเมนต์สามตัว
Damien_The_Unbeliever

7
@Damien_The_Unbeliever นั่นเป็นความจริงทางเทคนิค แต่มักเรียกว่าผู้ประกอบการที่ประกอบไปด้วยไตรภาค "นับตั้งแต่ดำเนินการนี้มักจะเป็นที่มีอยู่เพียงผู้ประกอบ ternary ในภาษาที่มันเป็นบางครั้งก็เรียกว่า 'ผู้ประกอบการที่ประกอบไปด้วยผู้ประกอบการที่มีเงื่อนไข '' ในบางภาษาดำเนินการนี้จะเรียกว่า'.. การดำเนินงาน Ternary
mguassa

Visual Basic ไม่มีผู้ประกอบการที่แท้จริง แต่คิดว่า IF และ IFF นั้นมีความสามารถในการทำงานเทียบเท่ากัน
Matt

@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" + หาก (เงื่อนไข $) {"true"} อื่น ๆ {"false"}และ"a" + (ถ้า (เงื่อนไข $) {"true"} อื่น {{false "}) งานนี้ (ฉันยังไม่แน่ใจว่าทำไม): " a "+ $ (ถ้า (เงื่อนไข $) {" true "} อื่น {" false " "})
Lamarth

12
@ Lamarth - ใช้งานได้เพราะ$()wrapper บังคับให้การประเมินคำสั่งเป็นนิพจน์ดังนั้นจะคืนค่าจริงของค่าเท็จเช่นเดียวกับผู้ประกอบการที่คาดหวังไว้ นี่คือสิ่งที่ใกล้เคียงที่สุดที่คุณจะได้รับใน PowerShell AFAIK
KeithS

4
ซึ่งแตกต่างจากบางส่วนของอื่น ๆ "ฟังก์ชั่นที่ไม่ใช่" คำตอบนี้จะยังให้แน่ใจว่าเฉพาะสาขาหนึ่งจะถูกดำเนินการ
2864740

63

PowerShell ที่ใกล้เคียงที่สุดที่ฉันสร้างขึ้นเพื่อเลียนแบบนั่นคือ:

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

15
({true}, {false})[!$condition]ดีกว่าเล็กน้อย (อาจ): ก) ลำดับดั้งเดิมของชิ้นส่วนจริงและเท็จ b) $conditionไม่จำเป็นต้องเป็นเพียง 0 หรือ 1 หรือ $ false, $ true ผู้ประกอบการ!แปลงตามที่จำเป็น คือ$conditionสามารถพูดได้ 42:! 42 ~ $ false ~ 0 ~ การแสดงออกครั้งแรก
Roman Kuzmin

1
ไม่เหมือนกันเลย @RomanKuzmin ตัวอย่างของ mjolinor ส่งคืนสตริง แต่ค่าสตริงเดียวกันจากตัวอย่างของเขาที่เสียบเข้ากับนิพจน์ของคุณจะส่งคืนบล็อกสคริปต์ :-(
Michael Sorens

5
โดย{true}และ{false}ฉันหมายถึง<true expression>และ<false expression>ไม่ใช่บล็อกสคริปต์ ขออภัยที่ไม่ถูกต้อง
Roman Kuzmin

1
ขอบคุณสำหรับคำชี้แจง @ RomanKuzmin - ตอนนี้ฉันเห็นคุณค่าในคำแนะนำของคุณแล้ว
Michael Sorens

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

ดูเหมือนจะไม่มีเหตุผลว่า 'decider' เป็น scriptblock หาก?:มีการประเมินเคยจะต้องมีการประเมินข้อโต้แย้ง หากไม่มีบล็อกสคริปต์ก็จะมีการใช้งานที่คล้ายกันเช่น (?: ($quantity -le 10) {.9} {.75})
2864740

นั่นเป็นคุณสมบัติเจ๋ง ๆ แต่มันสามารถทำให้สคริปต์ของคุณไม่ได้มาตรฐานเช่นสคริปต์หรือบางส่วนของมันอาจจะไม่ได้พอร์ตเว้นแต่ว่านิยาม alias จะถูกพอร์ตด้วยเช่นกัน คุณกำลังสร้างภาษาย่อยของคุณเอง ณ จุดนี้ ทั้งหมดนี้สามารถนำไปสู่ต้นทุนการบำรุงรักษาที่เพิ่มขึ้นเนื่องจากความซับซ้อนที่เพิ่มขึ้นและความสามารถในการอ่านลดลง
Vance McCorkle

1
@VanceMcCorkle จุดแข็ง ความอดทนแบบกำหนดเองนั้นคุ้มค่ากับราคาหากมีประโยชน์บ่อยครั้ง แต่ถ้าสถานการณ์เกิดขึ้นเพียงน้อยนิดฉันก็จะแสดงออกด้วย "ถ้า"
Edward Brey

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 รวมตัวกัน$message =: $foo ?? "foo was empty"`
Garrett Serack

ฉันรู้และฉันก็ฉันเพียงแค่แสดงความคิดเห็นว่าทำไมฉันจะใช้นามแฝงอื่น
nohwnd

15

ลองใช้คำสั่งswitchของ powershell เป็นทางเลือกโดยเฉพาะอย่างยิ่งสำหรับการกำหนดตัวแปร - หลายบรรทัด แต่อ่านได้

ตัวอย่าง,

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

1
นี่อาจเป็น verbose มากกว่าเล็กน้อย แต่มีประโยชน์ที่สำคัญกว่าคำตอบอื่น ๆ อีกมากมาย โดยเฉพาะอย่างยิ่งไม่มีการพึ่งพารหัสภายนอกหรือในฟังก์ชั่นที่ถูกคัดลอก / วางลงในสคริปต์หรือโมดูล
bshacklett

9

เนื่องจากตัวดำเนินการแบบไตรภาคมักใช้เมื่อกำหนดค่าจึงควรส่งคืนค่า นี่เป็นวิธีที่สามารถทำงานได้:

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

โง่ แต่ทำงาน นอกจากนี้โครงสร้างนี้สามารถใช้เพื่อเปลี่ยนค่า int ให้เป็นค่าอื่นได้อย่างรวดเร็วเพียงเพิ่มองค์ประกอบของอาร์เรย์และระบุนิพจน์ที่ส่งกลับค่าที่ไม่ใช่ค่าลบ 0


6
สิ่งนี้จะบังคับให้มีการประเมินความกระตือรือร้นของตัวเลือกซ้ายและขวา: สิ่งนี้แตกต่างจากการดำเนินการที่เหมาะสมมาก
2864740

6

เนื่องจากฉันใช้มาหลายครั้งแล้วและไม่เห็นมันอยู่ในรายการฉันจะเพิ่มส่วนของฉัน:

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

น่าเกลียดที่สุดของทั้งหมด!

แหล่งที่มา


4
สิ่งนี้จะบังคับให้มีการประเมินความกระตือรือร้นของตัวเลือกซ้ายและขวา: สิ่งนี้แตกต่างจากการดำเนินการที่เหมาะสมมาก
2864740

@ user2864740 นั่นเป็นเพราะไม่มีการดำเนินการแบบไตรภาคที่เหมาะสมใน PS นี่คือตัวแปรสังเคราะห์
Viggo Lundén

2
@ ViggoLundénการขาดผู้ประกอบการที่เหมาะสมไม่ลดความถูกต้องของความคิดเห็น นี้ "ตัวแปรสังเคราะห์" มีพฤติกรรมที่แตกต่างกัน
2864740

คุณพูดถูกและหลังจากอ่านความคิดเห็นของคุณอีกครั้งฉันเข้าใจว่ามันสามารถสร้างความแตกต่างได้จริง ขอบคุณ
sodawillow

5

ฉันเพิ่งปรับปรุง (open 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 -eq 4) หรือ scriptblock {$ b -eq 4}

หากตัวแปรในนิพจน์ตัวแปรเป็น $ null หรือไม่มีอยู่ นิพจน์ทางเลือกจะถูกประเมินเป็นเอาต์พุต


4

PowerShell ในปัจจุบันไม่มีInline Ifดั้งเดิม(หรือternary If ) แต่คุณสามารถพิจารณาใช้ cmdlet ที่กำหนดเองได้:

IIf <condition> <condition-is-true> <condition-is-false>
ดู: PowerShell Inline If (IIf)


ที่เชื่อมโยงการแสดงโพสต์วิธีการสร้างIIfฟังก์ชั่น อย่างไรก็ตามไม่มีIIfฟังก์ชันPowerShell / cmdlet มาตรฐาน
2864740

1
@ user2864740 ดังนั้นอะไร การที่ไม่รวมคำตอบนั้นเป็นคำตอบที่เป็นไปได้สำหรับคำถาม: " สิ่งที่จะเป็นวิธีที่ดีที่สุด (เช่นง่ายต่อการอ่านและการบำรุงรักษา) เพื่อให้ได้ผลลัพธ์เดียวกัน" แต่นอกเหนือจากนั้นลิงค์อ้างถึง IIfคำถาม (โพสต์เมื่อ 5 ก.ย. 14) ที่คล้ายกันมากกับสิ่งนี้ (ซ้ำกัน?) ส่วนที่เหลือของคำตอบในคำถามที่เชื่อมโยงอาจถูกมองว่าเป็นมูลค่าเพิ่ม (และหากไม่เป็นเช่นนั้นก็เพราะพวกเขาซ้ำกัน)
iRon

คำอธิบายเล็กน้อยไปไกลและนี่คือเหตุผลที่ทำให้ลิงค์หมดกำลังใจเพราะไม่ปิดเพื่อทำซ้ำหากไม่มีข้อมูลเพิ่มเติมเพิ่มเติม พิจารณาว่าคำอธิบายเล็ก ๆ น้อย ๆ จะช่วยเพิ่มคุณภาพของคำตอบลิงก์เปลือย : "Powershell ไม่สนับสนุน 'ternary' โดยตรง (^ แต่ดูคำตอบอื่น ๆ ) อย่างไรก็ตามมันเป็นไปได้ที่จะเขียนฟังก์ชั่นเช่น (^ ใช้ วิธีนี้หรือดูคำตอบอื่น ๆ ) .. "แต่นั่นไม่ใช่งานของฉันที่จะเพิ่ม คำตอบสามารถแก้ไข / ปรับปรุง / ฯลฯ ตามความเหมาะสม
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
    • ?? ถูกใช้ในภาษาอื่นเป็นตัวดำเนินการรวมศูนย์และฉันต้องการหลีกเลี่ยงความสับสน
  • ทำไมเราต้องการไพพ์ก่อนคำสั่ง?
    • เนื่องจากฉันใช้ประโยชน์จากไปป์ไลน์เพื่อประเมินสิ่งนี้เรายังคงต้องการตัวละครนี้เพื่อไปป์เงื่อนไขในฟังก์ชันของเรา
  • จะเกิดอะไรขึ้นถ้าฉันผ่านแถวลำดับ
    • เราได้รับผลลัพธ์สำหรับแต่ละค่า ie -2..2 |??? 'match' : 'nomatch'ให้: match, match, nomatch, match, match(เช่นเนื่องจากใด ๆ ที่ไม่ใช่ศูนย์ int ประเมินถึงtrue; ในขณะที่ศูนย์ประเมินถึงfalse)
    • หากคุณไม่ต้องการให้แปลงอาเรย์เป็นบูล ([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))"

โปรดทราบว่าค่าบูลีนไม่ควรเป็นคำที่นำในการคูณเช่น $ condition * "true" ฯลฯ จะไม่ทำงาน


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.