问题
I got excited with PowerShell ScriptBlock at first but I was confused recently with its executing ordering inside blocks. For example:
$test_block = {
write-host "show 1"
ps
write-host "show 2"
Get-Date
}
The output by calling $test_block.Invoke():
show 1
show 2
<result of command 'ps'>
<result of command 'get-date'>
Do commands who output something run first?
回答1:
This behaviour is because write-host doesn't put the output on the pipeline. The other commands are placed on the pipeline so are not output to the screen until the function (invoke) returns.
To get the behaviour I believe you were expecting, use write-output instead, the results of all the commands will then be returned in the pipeline.
$test_block = {
write-output "show 1"
ps
write-output "show 2"
Get-Date
}
$test_block.Invoke()
回答2:
To complement David Martin's helpful answer:
Indeed, Write-Host
is meant for writing to the host directly, which is the console (terminal) in a console window-based PowerShell session.
That is, Write-Host
intentionally bypasses PowerShell's success output stream, to which commands and expressions implicitly send their data.
Write-Output
is the cmdlet that writes to the success output stream, but its explicit use is rarely necessary; you could have just used "show 1"
and "show 2"
as-is.
Write-Host
is meant for creating user interfaces (printing (colored) messages to inform the user), not for outputting data.
See the bottom section of this answer for more information.
Script blocks ({ ... }
) are normally invoked with &
, the call operator, not via their .Invoke()
method.
.Invoke()
indeed collects all success-output stream data first, before outputting them on returning from the method call, whereas Write-Host
calls are immediate.
However, this behavior does not apply to invocation with &
:
Calls with &
- as well as direct invocation of commands in sequence - immediately output success-stream objects too, so you'll get the expected output ordering.
$test_block = {
write-host "show 1"
ps
write-host "show 2"
Get-Date
}
# Use of & to call the script block results in the expected output order.
& $test_block
# show 1
# <ps output>
# show 2
# <get-date output>
However, in PSv5+ there still can be an output-ordering problem, although its cause is unrelated:
Implicit tabular output (implied Format-Table
) for output types without predefined format data is asynchronous in an effort to determine suitable column widths.
See the following examples and this answer for more information.
# OK: Output ordering as expected:
# Get-Item outputs a System.IO.DirectoryInfo instance for which
# format data is predefined.
PS> Write-Host '------- 1'; Get-Item /; Write-Host '------- 2'
------- 1
Directory:
Mode LastWriteTime Length Name
---- ------------- ------ ----
d--hs- 12/5/2019 3:34 PM C:\
------- 2
# !! OUT OF SEQUENCE, because Select-Object creates [pscustomobject] instances
# !! for which no format data is predefined, and custom object with 4 or fewer
# !! properties triggers implicit Format-Table display.
PS> Write-Host '------- 1'; Get-Item / | Select-Object FullName; Write-Host '------- 2'
------- 1
------- 2
FullName
--------
C:\
You can solve this problem by piping the out-of-sequence command to Out-Host
, but note that this prevents further programmatic processing of its output:
# Using Out-Host fixes the output-ordering problem, but prints
# the data to the host (display) only.
PS> Write-Host '------- 1'; Get-Item / | Select-Object FullName | Out-Host; Write-Host '------- 2'
------- 1
FullName
--------
/
------- 2
See this answer for a potential - but suboptimal - solution that doesn't require Out-Host
and instead sends the for-display strings to the success output stream, achieving coloring via embedded VT (Virtual Terminal) escape sequences.
来源:https://stackoverflow.com/questions/19629003/command-execution-ordering-inside-a-powershell-scriptblock