在 powershell 管道中,如何等待 cmdlet 完成后再继续下一个?

Dan*_*ira 3 powershell

考虑以下函数:

function myFunction {
  100
  sleep 1
  200
  sleep 1
  300
  sleep 1
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,它将沿着管道一一发出这些值。

我想等待所有值发出后再继续。喜欢

myFunction | waitForThePreviousCommandToComplete | Format-Table 
Run Code Online (Sandbox Code Playgroud)

我希望上面的格式表接收整个数组,而不是一对一的项目。

在 Powershell 中是否可以实现?

mkl*_*nt0 5

使用 分组运算符(...),以便先完整收集命令的输出,然后再将其发送到成功输出流管道)。

# Due to (...), doesn't send myfunction's output to Format-Table until it has run 
# to completion and all its output has been collected.
(myFunction) | Format-Table

# Also works for entire pipelines.
(100, 200, 300 | ForEach-Object { $_; Start-Sleep 1 }) | Format-Table
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 如果您需要预先收集多个命令(管道)和/或语言语句的输出,请使用$(...), 子表达式运算符,例如
    $(Get-Date -Year 2020; Get-Date -Year 2030) | Format-Table; 下一点也适用于它。

  • (...)枚举收集的任何输出,即,如果收集的输出是可枚举的,则其元素将被一一发送成功输出流 - 尽管此时没有任何延迟。

    • 请注意,如果收集了两个或多个输出对象,则收集的输出始终是一个可枚举的(类型为 的数组),但在通常情况下,收集到一个本身是可枚举的单个对象时,它也可能是一个。[object[]]
    • 例如,(Write-Output -NoEnumerate 1, 2, 3) | Measure-Object报告 的计数3,即使Write-Output -NoEnumerate将给定数组输出为单个对象(没有(...)Measure-Object将报告1)。
  • 通常,命令(cmdlet、函数、脚本)流式传输其输出对象,即,一旦生成它们,就将它们一一发送到管道,同时命令仍在运行,就像您的函数一样,并且也作用于其管道一一输入。但是,某些 cmdlet总是首先收集所有输入对象,然后再开始发出输出对象,这是概念上的必要性:值得注意的示例是Sort-ObjectGroup-ObjectMeasure-Object,所有这些都必须在启动之前对其整个输入起作用发射结果。Format-Table当它通过开关时也是如此-AutoSize,接下来讨论。

  • 具体来说Format-Table,在 的情况下,您可以使用该-AutoSize开关强制首先收集所有输入,以便根据所有数据确定合适的显示列宽(默认情况下,Format-Table等待 300 毫秒。为了确定列宽,基于当时收到的输入数据的任何子集)。

    • 但是,这不适用于所谓的带外格式对象,特别是字符串原始.ToString().NET 类型,它们在接收时仍然会发出(通过其区域性不变的表示形式)。

    • 仅首先收集复杂对象(具有属性的对象),尤其是哈希表[pscustomobject]实例;例如:

      # Because this ForEach-Object call outputs complex objects (hashtables),
      # Format-Table, due to -AutoSize, collects them all first,
      # before producing its formatted output.
      100, 200, 300 | ForEach-Object { @{ num = $_ }; Start-Sleep 1 } |
        Format-Table -AutoSize
      
      Run Code Online (Sandbox Code Playgroud)

如果您想创建一个预先收集所有管道输入的自定义函数,您有两个选择:

  • 创建一个简单的函数,在其函数体中使用自动$input变量,该函数仅在收到所有输入隐式运行;例如:

    # This simple function simply relays its input, but 
    # implicitly only after all of it has been collected.
    function waitForThePreviousCommandToComplete { $input }
    
    # Output doesn't appear until after the ForEach-Object
    # call has emitted all its output.
    100, 200, 300 | ForEach-Object { $_; Start-Sleep 1 } | waitForThePreviousCommandToComplete
    
    Run Code Online (Sandbox Code Playgroud)
  • 在高级函数的上下文中,您必须通过块中分配的列表类型实例在块中迭代地手动收集所有输入,然后您可以在块中对其进行处理。processbeginend

    • 虽然使用简单函数$input显然更简单,但您可能仍然需要一个高级函数来获得它提供的所有额外好处(防止未绑定参数、参数验证、多个管道绑定参数……)。
    • 请参阅此答案的示例。