Null liên kết trong powershell


115

Có toán tử liên kết rỗng trong powershell không?

Tôi muốn có thể thực hiện các lệnh c # này trong powershell:

var s = myval ?? "new value";
var x = myval == null ? "" : otherval;

Câu trả lời:


133

Powershell 7+

Powershell 7 giới thiệu liên kết null nguyên bản, gán có điều kiện null và toán tử bậc ba trong Powershell.

Null Coalescing

$null ?? 100    # Result is 100

"Evaluated" ?? (Expensive-Operation "Not Evaluated")    # Right side here is not evaluated

Chuyển nhượng có điều kiện rỗng

$x = $null
$x ??= 100    # $x is now 100
$x ??= 200    # $x remains 100

Nhà điều hành bậc ba

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

Những phiên bản trước:

Không cần Tiện ích mở rộng cộng đồng Powershell, bạn có thể sử dụng câu lệnh if Powershell tiêu chuẩn dưới dạng biểu thức:

variable = if (condition) { expr1 } else { expr2 }

Vì vậy, để thay thế cho biểu thức C # đầu tiên của bạn:

var s = myval ?? "new value";

trở thành một trong những điều sau (tùy thuộc vào sở thích):

$s = if ($myval -eq $null) { "new value" } else { $myval }
$s = if ($myval -ne $null) { $myval } else { "new value" }

hoặc tùy thuộc vào những gì $ myval có thể chứa mà bạn có thể sử dụng:

$s = if ($myval) { $myval } else { "new value" }

và biểu thức C # thứ hai ánh xạ theo cách tương tự:

var x = myval == null ? "" : otherval;

trở thành

$x = if ($myval -eq $null) { "" } else { $otherval }

Công bằng mà nói, chúng không phải là rất linh hoạt và không dễ sử dụng như các dạng C #.

Bạn cũng có thể cân nhắc gói nó trong một hàm rất đơn giản để làm cho mọi thứ dễ đọc hơn:

function Coalesce($a, $b) { if ($a -ne $null) { $a } else { $b } }

$s = Coalesce $myval "new value"

hoặc có thể là IfNull:

function IfNull($a, $b, $c) { if ($a -eq $null) { $b } else { $c } }

$s = IfNull $myval "new value" $myval
$x = IfNull $myval "" $otherval

Như bạn có thể thấy, một hàm rất đơn giản có thể cung cấp cho bạn một chút tự do về cú pháp.

CẬP NHẬT: Một tùy chọn bổ sung cần xem xét trong hỗn hợp là một hàm IsTrue chung chung hơn:

function IfTrue($a, $b, $c) { if ($a) { $b } else { $c } }

$x = IfTrue ($myval -eq $null) "" $otherval

Sau đó, kết hợp đó là khả năng của Powershell để khai báo các bí danh trông hơi giống toán tử, bạn kết thúc với:

New-Alias "??" Coalesce

$s = ?? $myval "new value"

New-Alias "?:" IfTrue

$ans = ?: ($q -eq "meaning of life") 42 $otherval

Rõ ràng điều này sẽ không phù hợp với sở thích của mọi người, nhưng có thể là thứ bạn đang tìm kiếm.

Như Thomas lưu ý, một điểm khác biệt tinh tế khác giữa phiên bản C # và phiên bản ở trên là C # thực hiện việc ghi ngắn mạch các đối số, nhưng các phiên bản Powershell liên quan đến các hàm / bí danh sẽ luôn đánh giá tất cả các đối số. Nếu đây là một vấn đề, hãy sử dụng ifbiểu mẫu.


6
Điều đúng duy nhất tương đương với toán tử kết hợp là sử dụng câu lệnh if; vấn đề là bất kỳ cách tiếp cận nào khác đánh giá tất cả các toán hạng thay vì đoản mạch. "?? $ myval SomeReallyExpenisveFunction ()" sẽ gọi hàm ngay cả khi $ myval không null. Tôi cho rằng người ta có thể trì hoãn việc đánh giá bằng cách sử dụng scriptblocks, nhưng hãy lưu ý rằng scriptblock KHÔNG phải là đóng và mọi thứ bắt đầu trở nên rắc rối.
Thomas S. Trias

Không hoạt động ở chế độ nghiêm ngặt - nó ném The variable '$myval' cannot be retrieved because it has not been set..
BrainSlugs83

1
@ BrainSlugs83 Lỗi bạn đang gặp ở chế độ nghiêm ngặt không liên quan đến các tùy chọn liên kết rỗng được trình bày. Nó chỉ là tiêu chuẩn, Powershell kiểm tra xem một biến được xác định trước. Nếu bạn đặt $myval = $nulltrước khi thực hiện kiểm tra, lỗi sẽ biến mất.
StephenD ngày

1
Cảnh báo, powershell quirk, null nên luôn được so sánh (một cách vụng về) khi đặt null trước, tức là, $null -ne $a Hiện tại không thể tìm thấy tài liệu tham khảo phù hợp, nhưng đây là cách làm lâu dài.
dudeNumber4

90

PowerShell 7 trở lên

PowerShell 7 giới thiệu nhiều tính năng mới và chuyển từ .NET Framework sang .NET Core. Kể từ giữa năm 2020, nó vẫn chưa thay thế hoàn toàn các phiên bản PowerShell kế thừa do sự phụ thuộc vào .NET Core, nhưng Microsoft đã chỉ ra rằng họ dự định dòng Core sẽ thay thế dòng Framework kế thừa. Vào thời điểm bạn đọc thông báo này, một phiên bản PowerShell tương thích có thể được cài đặt sẵn trên hệ thống của bạn; nếu không, hãy xem https://github.com/powershell/powershell .

Theo tài liệu , các toán tử sau được hỗ trợ ngay trong PowerShell 7.0:

  1. Null-kết hợp :??
  2. Phân công liên kết rỗng :??=
  3. Đệ tam :... ? ... : ...

Những điều này hoạt động như bạn mong đợi đối với liên kết rỗng:

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

Kể từ khi toán tử bậc ba đã được giới thiệu, bây giờ có thể thực hiện điều sau đây, mặc dù không cần thiết khi bổ sung toán tử liên kết null:

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

Kể từ phiên bản 7.0, các tính năng sau cũng khả dụng nếu PSNullConditionalOperatorstính năng tùy chọn được bật , như được giải thích trong tài liệu ( 1 , 2 ):

  1. Quyền truy cập thành viên không có điều kiện cho các thành viên: ?.
  2. Quyền truy cập thành viên có điều kiện rỗng cho mảng và cộng sự: ?[]

Chúng có một số lưu ý:

  1. Vì đây là những thử nghiệm nên chúng có thể thay đổi. Chúng có thể không còn được coi là thử nghiệm vào thời điểm bạn đọc bài viết này và danh sách cảnh báo có thể đã thay đổi.
  2. Các biến phải được đặt trong ${}nếu theo sau bởi một trong các toán tử thử nghiệm vì dấu chấm hỏi được phép sử dụng trong tên biến. Không rõ liệu điều này có xảy ra hay không nếu / khi các tính năng chuyển sang trạng thái thử nghiệm (xem sự cố # 11379 ). Ví dụ: ${x}?.Test()sử dụng toán tử mới, nhưng $x?.Test()chạy Test()trên một biến có tên$x? .
  3. Không có ?(toán tử như bạn mong đợi nếu bạn đến từ TypeScript. Những điều sau sẽ không hoạt động:$x.Test?()

PowerShell 6 trở về trước

Các phiên bản PowerShell trước 7 có toán tử kết hợp rỗng thực sự, hoặc ít nhất là toán tử có khả năng thực hiện hành vi đó. Toán tử đó là -ne:

# Format:
# ($a, $b, $c -ne $null)[0]
($null, 'alpha', 1 -ne $null)[0]

# Output:
alpha

Nó linh hoạt hơn một chút so với toán tử kết hợp null, vì nó tạo ra một mảng tất cả các mục không phải 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 hoạt động tương tự, rất hữu ích để đếm các mục nhập rỗng:

($null, 'a', $null -eq $null).Length

# Result:
2

Nhưng dù sao, đây là một trường hợp điển hình để phản ánh ??toán tử của C # :

'Filename: {0}' -f ($filename, 'Unknown' -ne $null)[0] | Write-Output

Giải trình

Giải thích này dựa trên đề xuất chỉnh sửa từ một người dùng ẩn danh. Cảm ơn, dù bạn là ai!

Dựa trên thứ tự của các hoạt động, điều này hoạt động theo thứ tự sau:

  1. Các ,nhà điều hành tạo ra một mảng các giá trị để được kiểm tra.
  2. Các -nenhà điều hành lọc ra bất kỳ mục từ các mảng phù hợp với giá trị nhất định - trong trường hợp này, null. Kết quả là một mảng các giá trị không rỗng theo thứ tự như mảng được tạo ở Bước 1.
  3. [0] được sử dụng để chọn phần tử đầu tiên của mảng được lọc.

Đơn giản hóa điều đó:

  1. Tạo một mảng các giá trị có thể có, theo thứ tự ưu tiên
  2. Loại trừ tất cả các giá trị null khỏi mảng
  3. Lấy mục đầu tiên từ mảng kết quả

Cảnh báo

Không giống như toán tử liên kết rỗng của C #, mọi biểu thức có thể sẽ được đánh giá, vì bước đầu tiên là tạo một mảng.


Tôi đã kết thúc bằng cách sử dụng phiên bản sửa đổi của câu trả lời của bạn trong của tôi , vì tôi cần cho phép tất cả các trường hợp trong liên kết là rỗng, mà không đưa ra một ngoại lệ. Tuy nhiên, cách làm rất hay, tôi đã học được rất nhiều điều. :)
Johny Skovdal

Phương pháp này ngăn ngừa hiện tượng đoản mạch. Xác định function DidItRun($a) { Write-Host "It ran with $a"; return $a }và chạy ((DidItRun $null), (DidItRun 'alpha'), 1 -ne $null)[0]để xem điều này.
jpmc26

@ jpmc26 Vâng, đó là do thiết kế.
Zenexer

Bất kỳ nỗ lực nào để xác định một chức năng kết hợp cũng có khả năng loại bỏ hiện tượng đoản mạch phải không?
Chris F Carroll

8
Ahhhh Quyền ưu tiên toán tử của PowerShell giết chết tôi! Tôi luôn quên rằng toán tử dấu phẩy ,có mức độ ưu tiên cao như vậy. Đối với bất kỳ ai nhầm lẫn về cách hoạt động của điều này, ($null, 'alpha', 1 -ne $null)[0]cũng giống như (($null, 'alpha', 1) -ne $null)[0]. Trên thực tế, hai toán tử "dấu gạch ngang" duy nhất có mức độ ưu tiên cao hơn là -split-join(và dấu gạch ngang đơn vị? Tức là. -4Hoặc -'56').
Sao Diêm Vương

15

Đây chỉ là một nửa câu trả lời cho nửa đầu của câu hỏi, vì vậy một phần tư câu trả lời nếu bạn muốn, nhưng có một giải pháp thay thế đơn giản hơn nhiều cho toán tử liên kết null miễn là giá trị mặc định bạn muốn sử dụng thực sự là giá trị mặc định cho loại :

string s = myval ?? "";

Có thể được viết trong Powershell là:

([string]myval)

Hoặc là

int d = myval ?? 0;

dịch sang Powershell:

([int]myval)

Tôi thấy điều đầu tiên hữu ích khi xử lý một phần tử xml có thể không tồn tại và nếu nó tồn tại có thể có khoảng trắng không mong muốn bao quanh nó:

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

Việc ép kiểu thành chuỗi bảo vệ khỏi phần tử là null và ngăn ngừa mọi rủi ro Trim()thất bại.


([string]$null)-> ""có vẻ như một "hữu ích, nhưng không may tác dụng phụ" :(
user2864740


9

Nếu bạn cài đặt Mô-đun Tiện ích mở rộng Cộng đồng Powershell thì bạn có thể sử dụng:

?? là bí danh của Invoke-NullCoalescing.

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

?: là bí danh của Invoke-Ternary.

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

Trên thực tế, đó không phải là các lệnh PowerShell. Bạn đã kết hợp chúng với nhau với pscx:?: -> Invoke-Ternary
BartekB

... bỏ lỡ mã và kết quả thực tế ..;) Get-Command -Module pscx -CommandType bí danh | ở đâu {$ _. Tên -match '\ ?.' } | foreach {"{0}: {1}" -f $ _. Tên, $ _. Định nghĩa} ?: Gọi-Ternary ?? : Invoke-NullCoalescing
BartekB

Rất tiếc ... bạn hoàn toàn chính xác. Tôi thường quên rằng tôi thậm chí có tải mô-đun đó.
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

Thường thì tôi thấy rằng tôi cũng cần phải coi chuỗi rỗng là null khi sử dụng kết hợp. Cuối cùng tôi đã viết một hàm cho việc này, sử dụng giải pháp của Zenexer để liên kết với null đơn giản, và sau đó sử dụng Keith Hill để kiểm tra null hoặc rỗng, và thêm nó làm cờ để hàm của tôi có thể thực hiện cả hai.

Một trong những ưu điểm của hàm này là nó cũng xử lý việc có tất cả các phần tử null (hoặc rỗng) mà không đưa ra một ngoại lệ nào. Nó cũng có thể được sử dụng cho nhiều biến đầu vào tùy ý, nhờ vào cách PowerShell xử lý đầu vào mảng.

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

Điều này tạo ra các kết quả thử nghiệm sau:

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

Mã kiểm tra:

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

Gần nhất tôi có thể nhận được là: $Val = $MyVal |?? "Default Value"

Tôi đã triển khai toán tử liên kết null cho phần trên như thế này:

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

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

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

Các nhà điều hành ternary có điều kiện có thể được thực hiện một cách similary.

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

Và được sử dụng như: $Val = $MyVal |?: $MyVal "Default Value"

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.