Powershell 中的空合并

Powershell 中是否存在空合并运算符?

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

var s = myval ?? "new value";
var x = myval == null ? "" : otherval;
67152 次浏览

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

是 Invoke-NullCoalesching 的别名。

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

是 Invoke-Ternary 的别名。

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

Powershell 7 +

Powershell 7在 Powershell 引入了 本机 null 合并、 null 条件赋值和三元运算符。

零聚结

$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

显然这不是每个人都喜欢的,但可能是你想要的。

正如托马斯指出的那样,c # 版本和上述版本的另一个细微差别是,c # 执行短路参数,但是涉及函数/别名的 Powershell 版本总是计算所有参数。如果这是一个问题,使用 if表达式表单。

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

PowerShell 7及更高版本

PowerShell 7引入了许多新特性,并从。NET 架构,以。NET 核心。到2020年中期,它还没有完全取代 PowerShell 的遗留版本,因为它依赖于。NET 核心,但微软表示,他们打算为核心家庭最终取代遗留的框架家庭。在您阅读本文时,您的系统上可能已经预先安装了兼容的 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的可选特性是 启用,那么以下内容也是可用的,正如文档(12)中所解释的:

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

这里有几点需要注意的地方:

  1. 因为这些都是实验性的,它们可能会改变。在您阅读本文时,它们可能不再被认为是实验性的,而且警告列表可能已经改变。
  2. 如果后面跟着一个实验操作符,那么变量必须包含在 ${}中,因为在变量名中允许使用问号。目前还不清楚是否会出现这种情况,如果/当功能从实验状态毕业(见 第11379期)。例如,${x}?.Test()使用 new 操作符,但是 $x?.Test()对名为 $x?的变量运行 Test()
  3. 如果您来自 TypeScript,那么可能没有 ?(操作符。以下操作不起作用: $x.Test?()

PowerShell 6及更早版本

7之前的 PowerShell 版本确实有一个实际的空合并运算符,或者至少有一个能够执行这种行为的运算符。接线员是 -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 条目非常有用:

($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 合并运算符的方法,前提是你想使用的默认值实际上是类型的默认值:

string s = myval ?? "";

在 Powershell 可以这样写:

([string]myval)

或者

int d = myval ?? 0;

翻译过来就是 Powershell:

([int]myval)

我发现第一个在处理 xml 元素时很有用,这个 xml 元素可能不存在,而且如果存在的话,它周围可能有不想要的空格:

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

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

在使用聚合时,我经常发现还需要将空字符串视为 null。最后,我为此编写了一个函数,它使用 Zenexer的解决方案对简单的 null 合并进行合并,然后使用 Keith Hill的解决方案对 null 或空检查进行合并,并将其作为一个标志添加进来,这样我的函数就可以同时执行这两个操作。

这个函数的一个优点是,它还可以处理所有元素为 null (或空)的情况,而不会抛出异常。由于 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)

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

报税表

3

最接近的是 $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"

最后,PowerShell 7得到了 空合并赋值运算符

PS > $null ?? "a"
a
PS > "x" ?? "y"
x
PS > $x = $null
PS > $x ??= "abc"
PS > $x
abc
PS > $x ??= "xyz"
PS > $x
abc
@($null,$null,"val1","val2",5) | select -First 1

我也有过类似的问题。

当处理不存在的数组索引时,要简化这个过程要困难得多... ..。

或者更奇怪(但在 Powershell 也可能发生) 如果您试图从另一个尚未定义/创建的变量赋值..。

例子:

    $x = $y         # and $y id not defined/created yet
-or-
$x = $args[2]   # and args has less than 3 elements

奇怪的是,我“估计”了一些选择,其中一个变得非常方便... ..。
我认为这与 Powershell 的函数式编程近似有关
事实上,“未经挖掘”的堆栈价值仍然留在管道中供下一个消费者使用..

so this works flawlessly:
    $x = try{ $y }      catch { 1 }    # $x becomes 1
$x = try{ $a[ 2 ] } catch { 2 }    #$ x becomes 2
$x = try{ $a[ 2 ] } catch {   }    #$ x becomes $null
  • 它简化了很多代码。
  • 甚至有助于高级处理可选参数 *

希望能有帮助!