Consider the following function:
function myFunction {
100
sleep 1
200
sleep 1
300
sleep 1
}
As you can see it will emit those values, one by one down the pipeline.
But I want to wait for all the values to be emitted before going on. Like
myFunction | waitForThePreviousCommandToComplete | Format-Table
I want the Format-Table above to receive the entire array, instead of one-by-one items.
Is it even possible in Powershell?
CodePudding user response:
Use
(...)
, the grouping operator in order to collect a command's output in full first, before sending it to the success output stream:
# Doesn't send output to Format-Table until the pipeline inside
# (...) has run to completion and all its output has been collected.
(100, 200, 300 | ForEach-Object { $_; Start-Sleep 1 }) | Format-Table
Note:
Whatever output was collected by
(...)
is enumerated, i.e., if the collected output is an enumerable, its elements are emitted one by one to the success output stream - albeit without any delay at that point.- Note that the collected output is invariably an enumerable (an array) if two or more output objects were collected, but it also can be in the usual event that a single object that itself is an enumerable was collected.
- E.g.,
(Write-Output -NoEnumerate 1, 2, 3) | Measure-Object
reports a count of3
, even thoughWrite-Output -NoEnumerate
output the given array as a single object (without(...)
,Measure-Object
would report1
).
In the case of
Format-Table
, specifically, you can use the-AutoSize
switch in order force it to collect all input first, in order to determine suitable display column widths based on all data (by default,Format-Table
waits for 300 msecs. in order to determine column widths, based on whatever subset of the input data it has received by then).However, this does not apply to so-called out-of-band-formatted objects, notably strings and primitive .NET types, which are still emitted (by their culture-invariant
.ToString()
representation) as they're being received.Only complex objects (those with properties) are collected first, notably hashtables and
[pscustomobject]
instances; e.g.:# 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
If you want to create a custom function that collects all of its pipeline input up front, you have two options:
Create a simple function that uses the automatic
$input
variable in its function body, which implicitly runs only after all input has been received; e.g.:# 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
In the context of an advanced function, you'll have to manually collect all input, iteratively in the
process
block, via a list-type instance allocated in thebegin
block, which you can then process in theend
block.- While using a simple function with
$input
is obviously simpler, you may still want an advanced one for all the additional benefits it offers (preventing unbound arguments, parameter validation, multiple pipeline-binding parameters, ...). - See this answer for an example.
- While using a simple function with
CodePudding user response:
Sort waits until it has everything.
myFunction | sort-object
Or:
(myFunction)
$(myfunction1; myFunction2)
myFunction | format-table -autosize
myFunction | more
See also: How to tell PowerShell to wait for each command to end before starting the next?
CodePudding user response:
For some unknown reason, just putting the function inside brackets solved my problem:
(myFunction) | Format-Table