Question

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?

Was it helpful?

Solution

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()

OTHER TIPS

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, in argument (parsing) mode (like cmdlets and external programs), not via their .Invoke() method, which allows for more familiar syntax; e.g.:

  • & $test_block rather than $test_block.Invoke()
  • with arguments: & $test_block arg1 ... rather than $test_block.Invoke(arg1, ...)

Perhaps more importantly, using this operator-based invocation syntax (& { ... } ... or . { ... } ...) has the following advantages:

  • It preserves normal streaming semantics, meaning that success output (too) is emitted from the script block as it is being produced, whereas .Invoke() collects all success output first, in a [System.Collections.ObjectModel.Collection[psobject]] instance, which it then returns - by contrast, the Write-Host output goes straight to the display in both cases.

    • As a beneficial side effect, your specific output-ordering problem goes away, but note that in PSv5+ there can generally still 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 (your specific code happens to only use cmdlets with predefined format data).

    • See this answer for more information; a simple repro:

       [pscustomobject] @{ foo = 1 }; Write-Host 'Should print after, but prints first.'
      
  • It allows you to pass named arguments (e.g. -Foo Bar), whereas .Invoke() supports only positional (unnamed) ones (e.g. Bar).

  • It preserves normal semantics for script-terminating errors:

    # OK: & throw aborts the entire script; 'after' never prints.
    & { throw 'fatal' }; 'after'
    
    # !! .Invoke() "eats" the script-terminating error and
    # !! effectively converts it to a *statement*-terminating one.
    # !! Therefore, execution continues, and 'after' prints.
    { throw 'fatal' }.Invoke(); 'after'
    
  • Additionally, using operator-based invocation gives you the option to use ., the dot-sourcing operator, in lieu of &, so as to run a script block directly in the caller's scope, whereas an .Invoke() method call only runs in a child scope (as & does):

    # Dot-sourcing the script block runs it in the caller's scope.      
    . { $foo='bar' }; $foo # -> 'bar'
    

Use of .Invoke() is best limited to PowerShell SDK projects (which are typically C#-based, where use of PowerShell operators isn't an option).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top