Null coalescing in powershell

后端 未结 10 1103
遇见更好的自我
遇见更好的自我 2020-11-29 21:57

Is there a null coalescing operator in powershell?

I\'d like to be able to do these c# commands in powershell:

var s = myval ?? \"new value\";
var x         


        
相关标签:
10条回答
  • 2020-11-29 22:32

    Often I find that I also need to treat empty string as null when using coalesce. I ended up writing a function for this, which uses Zenexer's solution for coalescing for the simple null coalesce, and then used Keith Hill's for null or empty checking, and added that as a flag so my function could do both.

    One of the advantages of this function is, that it also handles having all elements null (or empty), without throwing an exception. It can also be used for arbitrary many input variables, thanks to how PowerShell handles array inputs.

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

    This produces the following test results:

    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
    

    Test code:

    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)
    
    0 讨论(0)
  • 2020-11-29 22:37

    This is only half an answer to the first half of the question, so a quarter answer if you will, but there is a much simpler alternative to the null coalescing operator provided the default value you want to use is actually the default value for the type:

    string s = myval ?? "";
    

    Can be written in Powershell as:

    ([string]myval)
    

    Or

    int d = myval ?? 0;
    

    translates to Powershell:

    ([int]myval)
    

    I found the first of these useful when processing an xml element that might not exist and which if it did exist might have unwanted whitespace round it:

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

    The cast to string protects against the element being null and prevents any risk of Trim() failing.

    0 讨论(0)
  • 2020-11-29 22:43
    @($null,$null,"val1","val2",5) | select -First 1
    
    0 讨论(0)
  • 2020-11-29 22:45

    Powershell 7+

    Powershell 7 introduces native null coalescing, null conditional assignment, and ternary operators in Powershell.

    Null Coalescing

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

    Null Conditional Assignment

    $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"
    

    Previous Versions:

    No need for the Powershell Community Extensions, you can use the standard Powershell if statements as an expression:

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

    So to the replacements for your first C# expression of:

    var s = myval ?? "new value";
    

    becomes one of the following (depending on preference):

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

    or depending on what $myval might contain you could use:

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

    and the second C# expression maps in a similar way:

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

    becomes

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

    Now to be fair, these aren't very snappy, and nowhere near as comfortable to use as the C# forms.

    You might also consider wrapping it in a very simple function to make things more readable:

    function Coalesce($a, $b) { if ($a -ne $null) { $a } else { $b } }
    
    $s = Coalesce $myval "new value"
    

    or possibly as, IfNull:

    function IfNull($a, $b, $c) { if ($a -eq $null) { $b } else { $c } }
    
    $s = IfNull $myval "new value" $myval
    $x = IfNull $myval "" $otherval
    

    As you can see a very simple function can give you quite a bit of freedom of syntax.

    UPDATE: One extra option to consider in the mix is a more generic IsTrue function:

    function IfTrue($a, $b, $c) { if ($a) { $b } else { $c } }
    
    $x = IfTrue ($myval -eq $null) "" $otherval
    

    Then combine that is Powershell's ability to declare aliases that look a bit like operators, you end up with:

    New-Alias "??" Coalesce
    
    $s = ?? $myval "new value"
    
    New-Alias "?:" IfTrue
    
    $ans = ?: ($q -eq "meaning of life") 42 $otherval
    

    Clearly this isn't going to be to everyone's taste, but may be what you're looking for.

    As Thomas notes, one other subtle difference between the C# version and the above is that C# performs short-circuiting of the arguments, but the Powershell versions involving functions/aliases will always evaluate all arguments. If this is a problem, use the if expression form.

    0 讨论(0)
提交回复
热议问题