考虑以下辅助函数:
Filter If-Null(
[Parameter(ValueFromPipeline=$true)]$value,
[Parameter(Position=0)]$default
) {
Write-Verbose "If ($value) {$value} Else {$default}"
if ($value) {$value} else {$default}
}
它基本上是一个null-coalescing operator实现为管道功能。它应该像这样工作:
PS> $myVar = $null
PS> $myVar | If-Null "myDefault" -Verbose
VERBOSE: If () {} Else {myDefault}
myDefault
然而,当我将$myVar
设置为空数组中的第一个元素时......
PS> $myVar = @() | Select-Object -First 1
......这应该与$null
...
PS> $myVar -eq $null
True
PS> -not $myVar
True
...然后管道不再起作用了:
PS> $myVar | If-Null "myDefault" -Verbose
根本没有输出。甚至不是冗长的印刷品。这意味着If-Null
甚至没有被执行。
所以好像@() | select -f 1
,-eq
到$null
,是有点不同 $null
,不知何故会破坏管道?
任何人都可以解释这种行为吗?我错过了什么?
PS> (@() | select -f 1).GetType() You cannot call a method on a null-valued expression. At line:1 char:1 + (@() | select -f 1).GetType() + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (:) [], RuntimeException + FullyQualifiedErrorId : InvokeMethodOnNull PS> (@() | select -f 1) | Get-Member Get-Member : You must specify an object for the Get-Member cmdlet. At line:1 char:23 + (@() | select -f 1) | Get-Member + ~~~~~~~~~~ + CategoryInfo : CloseError: (:) [Get-Member], InvalidOperationException + FullyQualifiedErrorId : NoObjectInGetMember,Microsoft.PowerShell.Commands.GetMemberCommand
PS> $PSVersionTable Name Value ---- ----- PSVersion 5.0.10586.117 PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...} BuildVersion 10.0.10586.117 CLRVersion 4.0.30319.42000 WSManStackVersion 3.0 PSRemotingProtocolVersion 2.3 SerializationVersion 1.1.0.1
Ansgar's explanation是正确的(可以在重复问题的mklement0's answer中找到更好的解释)。我只是想分享我对这个问题的解决方案。
我已修复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
答案 0 :(得分:3)
通过管道展开数组,以便分别传递每个数组元素。如果将空数组传递给管道,它实际上将展开为空,这意味着永远不会调用下游cmdlet,从而为您留下一个空变量。
您可以通过将$null
和@()
传递到一个循环来观察此行为,该循环只回显每个输入项的字符串:
PS C:\> @() | % { 'foo' } # no output here! PS C:\> $null | % { 'foo' } # output: foo foo
根据上下文,这与带有"值"的变量不同。 $null
。即使在大多数情况下,PowerShell也会自动转换"空"变量为$null
的值(如检查中所示),在将变量传递到管道中时,它不会这样做。在这种情况下,您仍然无法将任何内容传递到管道中,因此永远不会调用您的过滤器。