考虑以下函数:
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 中是否可以实现?
使用
分组运算符(...)
,以便先完整收集命令的输出,然后再将其发送到成功输出流(管道)。
# 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-Object
、Group-Object
和Measure-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)
在高级函数的上下文中,您必须通过块中分配的列表类型实例在块中迭代地手动收集所有输入,然后您可以在块中对其进行处理。process
begin
end
$input
显然更简单,但您可能仍然需要一个高级函数来获得它提供的所有额外好处(防止未绑定参数、参数验证、多个管道绑定参数……)。