Null รวมตัวกันใน powershell


115

มีตัวดำเนินการการรวมกันเป็นโมฆะใน powershell หรือไม่

ฉันต้องการที่จะสามารถทำคำสั่ง c # เหล่านี้ใน powershell:

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

Ternary Operator

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

เวอร์ชันก่อนหน้า:

ไม่จำเป็นต้องใช้ Powershell Community Extensions คุณสามารถใช้คำสั่ง Powershell if มาตรฐานเป็นนิพจน์:

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

เห็นได้ชัดว่านี่ไม่ใช่รสนิยมของทุกคน แต่อาจเป็นสิ่งที่คุณกำลังมองหา

ดังที่ Thomas ตั้งข้อสังเกตความแตกต่างที่ลึกซึ้งอีกประการหนึ่งระหว่างเวอร์ชัน C # และเวอร์ชันข้างต้นคือ C # ทำการลัดวงจรของอาร์กิวเมนต์ แต่เวอร์ชัน Powershell ที่เกี่ยวข้องกับฟังก์ชัน / นามแฝงจะประเมินอาร์กิวเมนต์ทั้งหมดเสมอ หากเป็นปัญหาให้ใช้ifแบบฟอร์มนิพจน์


6
สิ่งเดียวที่เทียบเท่ากับตัวดำเนินการการรวมตัวกันคือการใช้คำสั่ง if ปัญหาคือวิธีการอื่นใดจะประเมินตัวถูกดำเนินการทั้งหมดแทนการลัดวงจร "?? $ myval SomeReallyExpenisveFunction ()" จะเรียกใช้ฟังก์ชันแม้ว่า $ myval จะไม่เป็นโมฆะก็ตาม ฉันคิดว่าอาจมีคนชะลอการประเมินผลโดยใช้ scriptblocks แต่โปรดทราบว่าบล็อกสคริปต์ไม่ได้ปิดและสิ่งต่าง ๆ เริ่มยุ่งเหยิง
Thomas S. Trias

ไม่ทำงานในโหมดที่เข้มงวด - 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. การกำหนดค่า Null-coalescing :??=
  3. Ternary :... ? ... : ...

สิ่งเหล่านี้ทำงานตามที่คุณคาดหวังสำหรับการรวมกันเป็นโมฆะ:

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

เนื่องจากมีการแนะนำตัวดำเนินการ ternary จึงสามารถทำสิ่งต่อไปนี้ได้แม้ว่าจะไม่จำเป็นเนื่องจากการเพิ่มตัวดำเนินการเชื่อมต่อว่าง:

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

ในวันที่ 7.0 สิ่งต่อไปนี้จะพร้อมใช้งานหากเปิดใช้งานPSNullConditionalOperatorsคุณสมบัติเสริมดังที่อธิบายไว้ในเอกสาร ( 1 , 2 ):

  1. การเข้าถึงสมาชิกแบบไม่มีเงื่อนไขสำหรับสมาชิก: ?.
  2. การเข้าถึงสมาชิกที่มีเงื่อนไขว่างสำหรับอาร์เรย์และคณะ: ?[]

สิ่งเหล่านี้มีข้อแม้บางประการ:

  1. เนื่องจากเป็นการทดลองจึงอาจมีการเปลี่ยนแปลงได้ อาจไม่ถือว่าเป็นการทดลองอีกต่อไปเมื่อคุณอ่านสิ่งนี้และรายการคำเตือนอาจมีการเปลี่ยนแปลง
  2. ตัวแปรจะต้องอยู่ใน${}if ตามด้วยตัวดำเนินการทดลองตัวใดตัวหนึ่งเนื่องจากอนุญาตให้ใช้เครื่องหมายคำถามในชื่อตัวแปร ยังไม่ชัดเจนว่าจะเป็นเช่นนี้หรือไม่หาก / เมื่อคุณลักษณะต่างๆออกจากสถานะทดลอง (ดูปัญหา # 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

มันมีความหลากหลายมากกว่าตัวดำเนินการเชื่อมต่อแบบ null เล็กน้อยเนื่องจากมันสร้างอาร์เรย์ของรายการที่ไม่ใช่ค่าว่างทั้งหมด:

$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ดำเนินการจะกรองไอเท็มออกจากอาร์เรย์ที่ตรงกับค่าที่ระบุ - ในกรณีนี้คือ null ผลลัพธ์คืออาร์เรย์ของค่าที่ไม่ใช่ค่าว่างในลำดับเดียวกับอาร์เรย์ที่สร้างในขั้นตอนที่ 1
  3. [0] ใช้เพื่อเลือกองค์ประกอบแรกของอาร์เรย์ที่กรองแล้ว

ทำให้ง่ายขึ้น:

  1. สร้างอาร์เรย์ของค่าที่เป็นไปได้ตามลำดับที่ต้องการ
  2. ไม่รวมค่า null ทั้งหมดจากอาร์เรย์
  3. นำรายการแรกจากอาร์เรย์ผลลัพธ์

คำเตือน

ซึ่งแตกต่างจากตัวดำเนินการการรวมค่าว่างของ C # ทุกนิพจน์ที่เป็นไปได้จะได้รับการประเมินเนื่องจากขั้นตอนแรกคือการสร้างอาร์เรย์


ฉันลงเอยด้วยการใช้คำตอบของคุณในเวอร์ชันที่แก้ไขแล้วเนื่องจากฉันต้องการอนุญาตให้อินสแตนซ์ทั้งหมดในการรวมกันเป็นโมฆะโดยไม่มีข้อยกเว้น วิธีที่ดีมากในการทำมันเรียนรู้ฉันมาก :)
Johny Skovdal

วิธีนี้ช่วยป้องกันการลัดวงจร กำหนดfunction DidItRun($a) { Write-Host "It ran with $a"; return $a }และเรียกใช้((DidItRun $null), (DidItRun 'alpha'), 1 -ne $null)[0]เพื่อดูสิ่งนี้
jpmc26

@ jpmc26 ใช่นั่นคือการออกแบบ
Zenexer

ความพยายามใด ๆ ที่จะกำหนดฟังก์ชันการรวมตัวกันก็มีแนวโน้มที่จะกำจัดการลัดวงจรได้ใช่หรือไม่?
Chris F Carroll

8
ลำดับความสำคัญของตัวดำเนินการของ Ahhhh PowerShell ฆ่าฉัน! ฉันมักจะลืมว่าตัวดำเนินการลูกน้ำ,มีความสำคัญสูงเช่นนี้ สำหรับทุกคนที่สับสนเกี่ยวกับวิธีการทำงาน($null, 'alpha', 1 -ne $null)[0]จะเหมือนกับ(($null, 'alpha', 1) -ne $null)[0]. ในความเป็นจริงเพียงสองคนเท่านั้น "รีบ" ประกอบการที่มี precendence ที่สูงขึ้น-splitและ-join(และประเอก? ie. -4หรือ-'56')
พลูโต

15

นี่เป็นเพียงคำตอบครึ่งหนึ่งของคำถามครึ่งแรกดังนั้นคำตอบหนึ่งในสี่หากคุณต้องการ แต่มีทางเลือกอื่นที่ง่ายกว่ามากสำหรับตัวดำเนินการการรวมกันแบบ null โดยที่ค่าเริ่มต้นที่คุณต้องการใช้นั้นเป็นค่าเริ่มต้นสำหรับประเภท :

string s = myval ?? "";

สามารถเขียนใน Powershell เป็น:

([string]myval)

หรือ

int d = myval ?? 0;

แปลเป็น Powershell:

([int]myval)

ฉันพบว่าสิ่งแรกที่มีประโยชน์เหล่านี้เมื่อประมวลผลองค์ประกอบ xml ที่อาจไม่มีอยู่และหากมีอยู่อาจมีช่องว่างที่ไม่ต้องการล้อมรอบ:

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

Cast to string ป้องกันไม่ให้องค์ประกอบเป็นโมฆะและป้องกันความเสี่ยงที่จะTrim()ล้มเหลว


([string]$null)-> ""ดูเหมือนเป็น "ผลข้างเคียงที่มีประโยชน์ แต่โชคร้าย" :(
user2864740


9

หากคุณติดตั้งโมดูลส่วนขยายชุมชน Powershellคุณสามารถใช้:

?? เป็นนามแฝงสำหรับ Invoke-NullCoalescing

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

?: เป็นนามแฝงของ Invoke-Ternary

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

อันที่จริงนั่นไม่ใช่คำสั่ง PowerShell คุณได้ร่วมกับ pscx:?: -> Invoke-Ternary
BartekB

... พลาดรหัสจริงและผลลัพธ์ .. ;) Get-Command -Module pscx -CommandType นามแฝง | โดยที่ {$ _. Name -match '\ ?. ' } | foreach {"{0}: {1}" -f $ _. Name, $ _. Definition}?:: 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

บ่อยครั้งที่ฉันพบว่าฉันต้องถือว่าสตริงว่างเป็นโมฆะเมื่อใช้การรวมกัน ฉันลงเอยด้วยการเขียนฟังก์ชันสำหรับสิ่งนี้ซึ่งใช้โซลูชันของ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"

ฉันใช้ตัวดำเนินการการรวมกันเป็นโมฆะสำหรับข้างต้นดังนี้:

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

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

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

ตัวดำเนินการที่มีเงื่อนไขสามารถนำไปใช้ในลักษณะที่คล้ายคลึงกัน

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"

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