140

powershell中是否有空合并运算符?

我希望能够在 powershell 中执行这些 c# 命令:

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

10 回答 10

178

电源外壳 7+

Powershell 7在 Powershell 中引入了原生空值合并、空值条件赋值和三元运算符。

空值合并

$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 社区扩展,您可以使用标准的 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表达式形式。

于 2012-05-17T19:40:39.630 回答
99

PowerShell 7 及更高版本

PowerShell 7 引入了许多新功能并从 .NET Framework 迁移到 .NET Core。截至 2020 年年中,由于对 .NET Core 的依赖,它还没有完全取代旧版本的 PowerShell,但微软表示他们打算让 Core 系列最终取代旧的 Framework 系列。当您阅读本文时,您的系统中可能已预装了兼容版本的 PowerShell;如果没有,请参阅https://github.com/powershell/powershell

根据文档,PowerShell 7.0 支持以下开箱即用的运算符:

  1. 空合并??
  2. 空合并赋值??=
  3. 三元... ? ... : ...

这些工作与您对空值合并的期望一样:

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

由于引入了三元运算符,现在可以进行以下操作,尽管由于添加了空合并运算符,所以没有必要:

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

从 7.0 开始,如果启用PSNullConditionalOperators了可选功能,则以下内容也可用,如文档(1、2 中所述:

  1. 成员的空条件成员访问:?.
  2. 数组等的空条件成员访问:?[]

这些有一些警告:

  1. 由于这些是实验性的,因此它们可能会发生变化。当您阅读本文时,它们可能不再被认为是实验性的,并且警告列表可能已更改。
  2. 如果变量后面跟有实验运算符之一,则必须将变量括起来,${}因为变量名称中允许使用问号。目前尚不清楚如果/当功能从实验状态毕业时是否会出现这种情况(请参阅问题 #11379)。例如,${x}?.Test()使用 new 运算符,但在名为 的变量上$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 合并运算符更通用,因为它创建了一个包含所有非 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. 从数组中排除所有空值
  3. 从结果数组中取出第一项

注意事项

与 C# 的 null 合并运算符不同,每个可能的表达式都将被计算,因为第一步是创建一个数组。

于 2013-07-15T06:15:16.653 回答
15

这只是问题前半部分的一半答案,所以如果你愿意,可以回答四分之一,但是如果你想要使用的默认值实际上是类型的默认值,那么 null 合并运算符有一个更简单的替代方法:

string s = myval ?? "";

可以在 Powershell 中写为:

([string]myval)

或者

int d = myval ?? 0;

转换为 Powershell:

([int]myval)

在处理可能不存在的 xml 元素时,我发现其中的第一个很有用,如果它确实存在,它周围可能会有不需要的空格:

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

强制转换为字符串可防止元素为 null 并防止任何Trim()失败的风险。

于 2014-01-03T10:50:03.927 回答
13

$null, $null, 3 | Select -First 1

返回

3

于 2017-10-21T13:24:13.767 回答
9

如果您安装了Powershell 社区扩展模块,那么您可以使用:

?? 是 Invoke-NullCoalescing 的别名。

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

?: 是 Invoke-Ternary 的别名。

$x = ?: {$myval -eq $null} {""} {$otherval}
于 2012-05-16T18:07:30.593 回答
5

最后,PowerShell 7 获得了Null Coalescing 赋值运算符

PS > $null ?? "a"
a
PS > "x" ?? "y"
x
PS > $x = $null
PS > $x ??= "abc"
PS > $x
abc
PS > $x ??= "xyz"
PS > $x
abc
于 2019-10-22T16:01:49.573 回答
4
@($null,$null,"val1","val2",5) | select -First 1
于 2020-02-07T07:17:26.113 回答
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]
 }
于 2012-08-29T00:05:20.147 回答
2

我经常发现在使用合并时我还需要将空字符串视为 null。我最终为此编写了一个函数,它使用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)
于 2016-07-15T16:19:33.910 回答
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"

于 2018-09-07T08:59:16.730 回答