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?
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With