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
Finally, PowerShell 7 got Null Coalescing assignment operators!
PS > $null ?? "a"
a
PS > "x" ?? "y"
x
PS > $x = $null
PS > $x ??= "abc"
PS > $x
abc
PS > $x ??= "xyz"
PS > $x
abc
If you install the Powershell Community Extensions Module then you can use:
?? is the alias for Invoke-NullCoalescing.
$s = ?? {$myval} {"New Value"}
?: is the alias for Invoke-Ternary.
$x = ?: {$myval -eq $null} {""} {$otherval}
PowerShell 7 introduces many new features and migrates from .NET Framework to .NET Core. As of mid-2020, it hasn't completely replaced legacy versions of PowerShell due to the reliance on .NET Core, but Microsoft has indicated that they intend for the Core family to eventually replace the legacy Framework family. By the time you read this, a compatible version of PowerShell may come pre-installed on your system; if not, see https://github.com/powershell/powershell.
Per the documentation, the following operators are supported out-of-the-box in PowerShell 7.0:
??
??=
... ? ... : ...
These work as you would expect for null coalescing:
$x = $a ?? $b ?? $c ?? 'default value'
$y ??= 'default value'
Since a ternary operator has been introduced, the following is now possible, though it's unnecessary given the addition of a null coalescing operator:
$x = $a -eq $null ? $b : $a
As of 7.0, the following are also available if the PSNullConditionalOperators
optional feature is enabled, as explained in the docs (1, 2):
?.
?[]
These have a few caveats:
${}
if followed by one of the experimental operators because question marks are permitted in variable names. It's unclear if this will be the case if/when the features graduate from experimental status (see issue #11379). For example, ${x}?.Test()
uses the new operator, but $x?.Test()
runs Test()
on a variable named $x?
.?(
operator as you might expect if you're coming from TypeScript. The following won't work: $x.Test?()
PowerShell versions prior to 7 do have an actual null coalescing operator, or at least an operator that is capable of such behavior. That operator is -ne
:
# Format:
# ($a, $b, $c -ne $null)[0]
($null, 'alpha', 1 -ne $null)[0]
# Output:
alpha
It's a bit more versatile than a null coalescing operator, since it makes an array of all non-null items:
$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
works similarly, which is useful for counting null entries:
($null, 'a', $null -eq $null).Length
# Result:
2
But anyway, here's a typical case to mirror C#'s ??
operator:
'Filename: {0}' -f ($filename, 'Unknown' -ne $null)[0] | Write-Output
This explanation is based on an edit suggestion from an anonymous user. Thanks, whoever you are!
Based on the order of operations, this works in following order:
,
operator creates an array of values to be tested.-ne
operator filters out any items from the array that match the specified value--in this case, null. The result is an array of non-null values in the same order as the array created in Step 1.[0]
is used to select the first element of the filtered array.Simplifying that:
Unlike C#'s null coalescing operator, every possible expression will be evaluated, since the first step is to create an array.
$null, $null, 3 | Select -First 1
returns
3
Closest I can get is: $Val = $MyVal |?? "Default Value"
I implemented the null coalescing operator for the above like this:
function NullCoalesc {
param (
[Parameter(ValueFromPipeline=$true)]$Value,
[Parameter(Position=0)]$Default
)
if ($Value) { $Value } else { $Default }
}
Set-Alias -Name "??" -Value NullCoalesc
The conditional ternary operator could be implemented in a similary way.
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
And used like: $Val = $MyVal |?: $MyVal "Default Value"
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]
}