在PowerShell中的空值合并
在PowerShell中的空值合并
在PowerShell中是否有空值合并运算符?
我希望能够在PowerShell中执行以下C#命令:
var s = myval ?? "new value"; var x = myval == null ? "" : otherval;
PowerShell 7及以上版本
PowerShell 7推出了许多新功能并迁移到了.NET Core。截至2020年中期,它尚未完全取代由于依赖.NET Core而存在的旧版PowerShell,但微软已表示他们打算最终用核心系列替换旧的框架系列。当您阅读此文时,您的系统上可能预装了PowerShell的兼容版本;如果没有,请参见https://github.com/powershell/powershell。
根据文档,以下运算符在PowerShell 7.0中直接支持:
这些运算符的使用方式与您所期望的Null-合并相同:
$x = $a ?? $b ?? $c ?? 'default value' $y ??= 'default value'
由于已经引入了三元运算符,以下是现在可能的,但由于已添加了Null-合并运算符,这是不必要的:
$x = $a -eq $null ? $b : $a
自7.0以来,如果启用了可选特性PSNullConditionalOperators
,还可以使用以下内容,如文档所述1, 2):
- 成员的空条件访问:
?.
- 数组等的空条件访问:
?[]
这些具有一些警告:
- 由于这些是实验性的,它们可能会发生变化。当您阅读本文时,它们可能已不再是实验性的,并且警告列表可能已更改。
- 如果后面跟着实验性运算符,则变量必须用
${}
括起来,因为变量名允许问号。如果/当这些特性毕业时,不清楚是否会是这种情况(请参见issue#11379)。例如,${x}?.Test()
使用新运算符,但$x?.Test()
在一个名为$x?
的变量上运行Test()
。 - 没有(
运算符,如果您来自TypeScript,您可能会期望。以下功能不起作用:$x.Test?()
PowerShell 6及更早版本
在PowerShell 7之前的PowerShell版本确实具有一个实际的Null-合并运算符,或者至少具有这种行为的运算符。该运算符为-ne
:
# Format: # ($a, $b, $c -ne $null)[0] ($null, 'alpha', 1 -ne $null)[0] # Output: alpha
这个方法比空合并运算符更加灵活,因为它会把所有非空项放到一个数组中:
$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
说明
这篇说明是基于一个匿名用户的编辑建议。谢谢你,不知名的好心人!
按照操作顺序,该方法按以下步骤进行:
,
运算符创建一个要测试的值数组。-ne
运算符从数组中过滤出任何与指定值(这里是 null)匹配的项目。结果是按照步骤 1 中创建的数组顺序的非空值数组。- 使用
[0]
选择过滤后数组的第一个元素。
简单来说:
- 创建一个可能值的数组,按照首选顺序
- 从数组中排除所有 null 值
- 从生成的数组中选择第一个项目
注意事项
与 C# 的空合并运算符不同,因为第一步是创建一个数组,所以会评估每个可能的表达式。
Powershell 7+
Powershell 7 引入了本地的空合并、空条件赋值和三元运算符。
空合并
$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
三元运算符
$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 表达式形式。