8

In some cases, if I try to pause or sleep after a Select-Object command, the pause/sleep occurs before the command.

For example, with

Get-NetAdapter | Select-Object Name,Status
Pause

or

Get-NetAdapter | Select-Object Name,Status | Where-Object {$_ -ne $null}
Pause

the output is:

Press Enter to continue...:

Name     Status
----     ------
Wi-Fi    Up
Ethernet Disconnected

Whereas with

Get-NetAdapter | Select-Object Name,Status | Format-Table
Pause

the output is:

Name     Status
----     ------
Wi-Fi    Up
Ethernet Disconnected

Press Enter to continue...:

What's going on here? Is this a bug or a feature?

user4003407
  • 21,204
  • 4
  • 50
  • 60
Mica
  • 383
  • 3
  • 14

2 Answers2

9

What you see is a consequences of new PowerShell v5 feature. Format-Table now collect input for 300 milliseconds to find better column width. It work this way even if you explicitly specify -AutoSize:$false.

When you type command in command prompt, that command implicitly piped to single instance of Out-Default command. The Out-Default command then decide how to format objects and print them on PowerShell host (console). So that, even if you does not use Format-Table directly in your code, that does not mean that you does not have Format-Table in your pipeline. Out-Default can decide to format objects as table and use Format-Table internally.

Custom objects with four or less properties and without custom formatting defined for them in format files are formatted as table. By using Select-Object with two properties you produce exactly that objects.

PowerShell pipeline is single-threaded. That means that Format-Table can not just output all collected objects when 300 milliseconds interval elapsed. Format-Table have to wait till you pipe next item to it (process block invoked) or end of pipeline reported (end block invoked).

PS> Get-NetAdapter | Select-Object Name,Status
>>> Pause
>>> [PSCustomObject]@{Name='Some long name';Status='Some long status'} #1
>>> Pause
>>> [PSCustomObject]@{Name='Even longer name';Status='Even longer status'}
>>> Pause

Press Enter to continue...:
Name           Status
----           ------
Ethernet       Up
Some long name Some long status
Press Enter to continue...:
Even longer... Even longer s...
Press Enter to continue...:


PS>

Implicit Format-Table does not print anything (strictly saying it print empty line) before first Pause because it still waiting for more input objects (300 milliseconds not yet elapsed) to decide on column width. When first object (#1) come after 300 milliseconds interval (assuming that you are not to fast on pressing Enter), then Format-Table decide on column width and print all collected objects. Any further objects will be printed without delay, but them can not affect column width anymore. If value is to big for column it will be truncated.

PS> Get-NetAdapter | Select-Object Name,Status | Format-Table
>>> Pause

Name     Status
----     ------
Ethernet Up


Press Enter to continue...:
PS>

With this code, end block of explicit Format-Table will be executed before Pause. In end block Format-Table know that it already got all the input, so it can decide on column width and output all collected objects right away. Implicit Out-Default see that formatting objects from Format-Table output, and Out-Default know that them does not need any addition formatting and print them on host (console) right away as well. So whole table got printed before Pause invoked.

Notice the difference in placement of end of table mark (two empty lines). In first example it placed after last Pause. It is because implicit Format-Table still active and still wait, that you pass additional object to it. Only when your command fully completed Format-Table acknowledge end of input and output end of table mark. In second example, explicit Format-Table completes before Pause, so whole table (including end of table mark) got printed before Pause command.

The difference in placement of end of table mark can be noticed it previous versions of PowerShell as well.

user4003407
  • 21,204
  • 4
  • 50
  • 60
2

Format-Table has nothing to do with that I just try the following in PowerSell V 4.0 and PowerShel V5.0 and the problem can be reproduced :

Get-Process |Select-Object -Property name ; pause

a turn arround is :

Get-Process |Select-Object -Property Name  |%{Write-host $_.name};pause

Here again pause is run first :

Get-Process |%{$_.name | Set-Content 'c:\temp\test.txt';$_} |Select-Object -Property Name  ;pause

But not here

Get-Process |%{$_.name | Set-Content 'c:\temp\test.txt';Start-Sleep -Milliseconds 1;$_} |Select-Object
-Property Name  ;pause

For me, in PowerShell V5.0 everything works like if the host is not needed in instructions that are pipelined then these instructions are run asynchronosly.

I would have like that people as @Keith Hill have a look to this behavior.

JPBlanc
  • 70,406
  • 17
  • 130
  • 175
  • 1
    Everything implicitly piped to `Out-Default`, so your first code equivalent to `.{Get-Process |Select-Object -Property name ; pause}|Out-Default`. And `Out-Default` decide to use `Format-Table` to format your output. So in fact you have `.{Get-Process |Select-Object -Property name ; pause}|Format-Table`. If you change `Format-Table` to `Format-List` or explicitly set column width `Format-Table @{e='Name';w=50}`, then `pause` will be invoked after output. If you add timing on every output object, then you will see, that start outputting after 300 milliseconds interval. – user4003407 Jan 18 '16 at 07:17
  • `.{1..20|ForEach-Object {$SW=[Diagnostics.Stopwatch]::StartNew()}{Write-Host "Before writing '$_' ($($SW.ElapsedMilliseconds))";[PSCustomObject]@{Property=$_};Write-Host "After writing '$_' ($($SW.ElapsedMilliseconds))";Start-Sleep -Milliseconds 20}}|Format-Table` – user4003407 Jan 18 '16 at 07:18
  • Your first code is not cohérent with your sentence. Everything implicitly piped to `Out-Default`, so my first would be code equivalent to `{Get-Process |Select-Object -Property name |Out-Default; pause}`. Then can you find in Microsoft documentation where it's written that the out-default call the format-table ? I can read :"The Out-Default cmdlet sends output to the default formatter and the default output cmdlet." – JPBlanc Jan 18 '16 at 08:11
  • @PetSerAl MoreOver : In Windows 10 (PS V5.0) the following works : `Get-Process |Select-Object -Property Name |Format-Table;pause`. That is to say the pause is executed after and there is an explicit `format-table`, but not this `Get-Process |Select-Object -Property Name ;pause` the pause is executed before and there is no `format-table` – JPBlanc Jan 18 '16 at 08:17
  • 1
    Command as whole piped to single `Out-Default` instance. You can clearly see it by overriding `Out-Default`. `function Out-Default{begin{Write-Host B}process{Write-Host P}end{Write-Host E}}`. With that command `1,2;3,4` you will see B, P, P, P, P, E. So it `.{1,2;3,4}|Out-Default` and not `1,2|Out-Default;3,4|Out-Default`. And I does not say that build-in `Out-Default` always choose to use `Format-Table`. I only say it choose `Format-Table` for your output (custom object with single property with no formatting defined in format files). – user4003407 Jan 18 '16 at 08:27
  • 1
    `.{Get-Process |Select-Object -Property Name |Format-Table;pause}|Out-Default`: `Format-Table` applied **only** to this `Get-Process |Select-Object -Property Name`. When end block of `Format-Table` called (and that clearly happens before next command `pause` called) it output any holding objects. `Out-Default` see formatting object and choose not to use any addition formatting on them and print them on console without delay. – user4003407 Jan 18 '16 at 08:41
  • 1
    `.{Get-Process |Select-Object -Property Name ;pause}|Out-Default`: `Get-Process |Select-Object -Property Name` output custom objects with single property with no formatting defined in format files and `Out-Default` choose to use `Format-Table` on them. `Format-Table` hold objects for 300 milliseconds to decide on column width. `Get-Process|Select-Object -Property Name;pause;[PSCustomObject]@{Name='On that object column width for Name desided'};[PSCustomObject]@{Name='That string will be cutted as it does not fit column width for Name column'}` – user4003407 Jan 18 '16 at 08:52