为什么这个过滤器不能接受管道中的某些$null值



上下文

考虑以下辅助功能:

Filter If-Null(
[Parameter(ValueFromPipeline=$true)]$value,
[Parameter(Position=0)]$default
) {
Write-Verbose "If ($value) {$value} Else {$default}"
if ($value) {$value} else {$default}
}

它基本上是一个作为管道函数实现的null合并运算符。它应该是这样工作的:

PS> $myVar = $null
PS> $myVar | If-Null "myDefault" -Verbose
VERBOSE: If () {} Else {myDefault}
myDefault

然而,当我将$myVar设置为空数组中的第一个元素时。。。

PS> $myVar = @() | Select-Object -First 1

其应有效地与CCD_ 2相同。。。

PS> $myVar -eq $null
True
PS> -not $myVar
True

则管道不再工作:

PS> $myVar | If-Null "myDefault" -Verbose

根本没有输出。甚至连冗长的印刷品都没有。这意味着If-Null甚至没有被执行。

问题

所以看起来@() | select -f 1,虽然是-eq$null,但是不是有点不同$null会以某种方式破坏管道?

有人能解释这种行为吗?我错过了什么?

附加信息

PS>(@()|选择-f 1).GetType()不能对空值表达式调用方法。行:1字符:1+(@()|选择-f 1).GetType()+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+CategoryInfo:InvalidOperation:(:)[],RuntimeException+FullyQualifiedErrorId:InvokeMethodOnNullPS>(@()|选择-f 1)|获取会员Get-Member:必须为Get-Member cmdlet指定一个对象。第1行字符:23+(@()| select-f 1)|获取成员+~~~~~~~~~~+CategoryInfo:CloseError:(:)[Get-Member],InvalidOperationException+FullyQualifiedErrorId:NoObjectInGetMember,Microsoft.PowerShell.Commands.GetMemberCommand
PS>$PSVersionTable名称值---------PSV版本5.0.10586.117PSCompatibleVersions{1.0、2.0、3.0、4.0…}构建版本10.0.1056.117CLR版本4.0.30319.42000WSManStackVersion 3.0PSRemotingProtocol版本2.3SerializationVersion 1.1.0.1

解决方案

Ansgar的解释是正确的(在mklement0对重复问题的回答中可以找到更好的解释)。我只是想分享我对这个问题的解决方案。

我修复了If-Null,使其即使在未处理任何内容时也返回$default

Function If-Null(
[Parameter(ValueFromPipeline = $true)]$value, 
[Parameter(Position = 0)]$default
) {
Process { 
$processedSomething = $true
If ($value) { $value } Else { $default } 
}
# This makes sure the $default is returned even when the input was an empty array or of
# type [System.Management.Automation.Internal.AutomationNull]::Value (which prevents
# execution of the Process block).
End { If (-not $processedSomething) { $default }}
}

此版本现在可以正确处理空管道结果:

PS> @() | select -f 1 | If-Null myDefault
myDefault

数组是通过管道展开的,因此每个数组元素都是单独传递的。如果将一个空数组传递到管道中,它基本上会被展开为空,这意味着永远不会调用下游cmdlet,从而留下一个空变量。

您可以通过将$null@()传递到一个循环中来观察这种行为,该循环只为每个输入项回显一个字符串:

PS C:\>@()|%{'foo'}#此处无输出PS C:\>$null |%{'foo'}#output:foofoo

根据上下文,这与具有"值"$null的变量不同。尽管在大多数情况下,PowerShell会自动将"空"变量转换为值$null(如检查中所示),但在将变量传递到管道中时,它不会这样做。在这种情况下,您仍然不会将任何内容传递到管道中,因此您的过滤器永远不会被调用。

相关内容

  • 没有找到相关文章

最新更新