J. Breneman
J. Breneman

Reputation: 61

While working in Powershell, how do I pause between list items?

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

Answers (2)

mklement0
mklement0

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.

    • Note that non-PowerShell .NET methods only ever produce success output (which is their return value) and only ever communicate failure via exceptions, which become statement-terminating PowerShell errors.

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.

    • Note that to-host output with 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

Doug Maurer
Doug Maurer

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

Related Questions