【问题标题】:Powershell Foreach-Object behaves abnormaly on null values [duplicate]Powershell Foreach-Object 在空值上表现异常[重复]
【发布时间】:2019-05-17 07:02:26
【问题描述】:

Foreach-Object 在不同情况下对 $null 值的处理方式不同。理想的行为是什么?

## Case 1: 
 $someitem = $null 
 if($null -EQ $someitem) { write-host "true" } # prints true
 $someitem | ForEach-Object { write-host "hello" } # prints hello
## Case 2 :
 $items = Get-ChildItem -Path "someNotExistingPath" # throws error
 if($null -EQ $items) { write-host "true"} # prints true
 $items | ForEach-Object { write-host "hello" } # prints nothing

预期的行为是 foreach-object 都打印“hello”,但在我们尝试为 $items 分配一些值的情况下,它在 foreach 循环中表现异常

【问题讨论】:

  • $someitem 是单数形式的 $null(与 1 的数组相同),$items 是一个空数组,因此 $items | ForEach-Object 有 0 个元素要迭代

标签: powershell foreach null


【解决方案1】:

我通过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 的用法。希望社区没问题。

希望对您有所帮助。

【讨论】:

    猜你喜欢
    • 2020-10-06
    • 2018-02-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多