上下文
考虑以下辅助功能:
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
(如检查中所示),但在将变量传递到管道中时,它不会这样做。在这种情况下,您仍然不会将任何内容传递到管道中,因此您的过滤器永远不会被调用。