Reputation: 61
I have been working on this for a while and I cannot find this utilization anywhere. I am using a powershell array and the foreach statement:
@('program 1','program 2','program 3').foreach{winget show $_ -args}
I then want to have a pause between each one so I added ;sleep 1
This does not work. It pauses for 3s (based on this eg.) and then lists the items. What am I missing here?
Upvotes: 3
Views: 232
Reputation: 438273
Doug Maurer's helpful answer provides effective solutions.
As for an explanation of the behavior you saw:
The intrinsic .ForEach()
method first collects all success output produced by the successive, per-input-object invocations of the script block ({ ... }
) passed to it, and only after having processed all input objects outputs the collected results to the pipeline, i.e. the success output stream, in the form of a [System.Collections.ObjectModel.Collection[psobject]]
instance.
In other words:
Unlike its cmdlet counterpart, the ForEach-Object
cmdlet, the .ForEach()
method does not emit output produced in its script block instantly to the pipeline.
As with any method, success output is only sent to the pipeline when the method returns.
By contrast, the following do take instant effect inside a .ForEach()
call's script block:
Suspending execution temporarily, such as via a Start-Sleep
Forcing instant display (host) output, such as via Out-Host
or Write-Host
.
Out-Host
prevents capturing the output altogether, whereas Write-Host
output, in PowerShell v5+, can only be captured via the information output stream (number 6
).Writing to an output stream other than the success output stream, such as via Write-Error
, Write-Warning
or Write-Verbose -Verbose
.
Alternatively, you may use the foreach
statement, which, like the ForEach-Object
cmdlet, also instantly emits output to the success output stream:
# Stand-alone foreach statement: emits each number right away.
foreach ($i in 1..3) { $i; pause }
# In a pipeline, you must call it via & { ... } or . { ... }
# to get streaming behavior.
# $(...), the subexpression operator would NOT stream,
# i.e. it would act like the .ForEach() method.
& { foreach ($i in 1..3) { $i; pause } } | Write-Output
Upvotes: 3
Reputation: 8868
Indeed it doesn't seem to respect the order, I don't know the technical reason why. You could either use a normal ForEach-Object
'program 1','program 2','program 3' | ForEach-Object {
winget show $_
sleep 1
}
or force the output to go to the console instead of being "buffered"
('program 1','program 2','program 3').ForEach{
winget show $_ | Out-Host
sleep 1
}
Upvotes: 4