我通过Trace-Command追踪了$items | forEach-Object { Write-host "hello"}和$null | ForEach-Object { Write-Host "hello"}之间的区别。
PS C:>Trace-Command -Name parameterbinding -Expression { $items | ForEach-Object { write-host "hello" } } -PSHost
DEBUG: ParameterBinding Information: 0 : BIND NAMED cmd line args [Out-Null]
DEBUG: ParameterBinding Information: 0 : BIND POSITIONAL cmd line args [Out-Null]
DEBUG: ParameterBinding Information: 0 : MANDATORY PARAMETER CHECK on cmdlet [Out-Null]
....
PS C:> Trace-Command -Name parameterbinding -Expression { $null | ForEach-Object { write-host "hello" } } -PSHost
DEBUG: ParameterBinding Information: 0 : BIND NAMED cmd line args [ForEach-Object]
DEBUG: ParameterBinding Information: 0 : BIND POSITIONAL cmd line args [ForEach-Object]
DEBUG: ParameterBinding Information: 0 : BIND arg [ write-host "hello" ] to parameter [Process]
DEBUG: ParameterBinding Information: 0 : Binding collection parameter Process: argument type [ScriptBlock], parameter type [System.Management.Automation.ScriptBlock[]], collection type Array, element type [System.Management.Automation.ScriptBlock], no
似乎$items 指向Out-Null cmdlet,显示方式如下:
DEBUG: ParameterBinding Information: 0 : BIND NAMED cmd line args [Out-Null]
因此,Get-ChildItem 似乎会在出现错误时返回对Out-Null 的引用。如果将此与$null | ForEach-Object ... 进行比较,您会发现ForEach-Object 将被直接调用:
DEBUG: ParameterBinding Information: 0 : BIND NAMED cmd line args [ForEach-Object]
还有什么有趣的,如果您将ForEach-Object 与-InputObject 参数一起使用,代码将按要求工作:
PS C:> Trace-Command -Name parameterbinding -Expression { ForEach-Object -InputObject $items -Process { write-host "hello" } } -PSHost
DEBUG: ParameterBinding Information: 0 : BIND NAMED cmd line args [ForEach-Object]
DEBUG: ParameterBinding Information: 0 : BIND arg [] to parameter [InputObject]
所以我的“猜测”如下。如果出现错误(Get-ChildItem),您不会在下面的代码中打印输出:
PS C:\> Get-ChildItem -Path "notExisting" | ForEach-Object { Write-Host "found" }
这完全有道理,Get-ChildItem“调用”Out-Null 将清除管道,这将破坏管道链(= 如果没有找到,则不会打印任何内容。
基于此,调用语句$items = Get-ChildItem -Path "someNotExistingPath",但Get-ChildItem返回一个不等于$null的空类型。执行此代码if($null -EQ $items) 时,PowerShell 或多或少会执行Get-ChildItem-null-type 到$null 的隐式转换。当涉及到这个调用$items | ForEach-Object 时,不应将任何其他内容发送到管道,因为$items 包含Out-Null 的结果。
更新:
同时@iRon 还添加了一个重复链接,其中解释了details。我会保留答案,因为这个link 没有显示Trace-Command 的用法。希望社区没问题。
希望对您有所帮助。