It looks like you're seeing a very unfortunate side effect of the problematic 300-millisecond delay that was introduced to implicit use of Format-Table
in PowerShell v5 in order to better calculate suitable column widths, which is detailed in this answer and discussed in GitHub issue #4594; however, your symptom is so problematic that it warrants a new bug report: GitHub issue #13985.
Because your $testlist
array contains objects that have fewer than 4
properties (namely just one in your case) and because the type of your objects ([pscustomobject]
) has no formatting data associated with it, PowerShell implicitly uses Format-Table
for output formatting.
By the time the throw
statement is encountered, the 300-millisecond delay has not elapsed yet, and because throw
terminates the runspace[1], the Format-Table
output (and the subsequent Write-Output
output) are never shown.
The - imperfect - workaround is to force output to be synchronous, which can be done with one of three ways:
Use Out-Host
, which uses the default output behavior synchronously, but it also means that from inside PowerShell you won't be able to capture or redirect the output; however, when called from the outside, via PowerShell's CLI, Out-Host
output goes to stdout and can be captured too.
Use Format-Table
explicitly. This also works fine for display, but changes the output from your data to unrelated objects that are formatting instructions, which PowerShell itself translates to the usual, rich display formatting, but these are useless if you wanted to capture the output - your original data is lost.
If you want to least allow capturing the formatted string representations of your original objects from inside PowerShell, you can use Out-String
, which outputs the formatted representation as a single, multi-line string (unfortunately, with an extra trailing newline); when called from outside PowerShell, the effect will in essence be the same as calling Out-Host
.
Note that in your case the workaround need only be applied to the $testlist
statement:
# Or ... | Out-Host or ... | Format-Table - see comments above.
$testlist | Out-String
There's an alternative, but more obscure workaround that relies on using Start-Sleep
to wait at least 300 ms. and outputting at least one more object after the implicit Format-Table
call, as demonstrated in this simplified example:
'before'
[pscustomobject] @{ foo = 1 }
Start-Sleep -Milliseconds 300 # wait for implicit Format-Table
'after' # force output of the table by outputting at least one more object
throw "error"
The advantage of this workaround is that your original objects are preserved in the output this way, which would allow you to capture them as such from inside PowerShell with, say,
$output = try { ./someScript.ps1 } catch { Write-Error $_ }
Finally, your code can be streamlined; to put it all together:
# Use *implicit* output - no need for Write-Output
# If you do use Write-Output: separate the arguments with *spaces*,
# don't put (...) around the argument list.
"test output 1"
# ...
# Implicitly capture the foreach loop output in an array.
# This is much more efficient than using += to "extend" an array
# in a loop (which requires creating a *new* array every time).
[array] $testlist =
foreach ($count in 1..6) {
# Simpler and more efficient PSv3+ method for constructing
# custom objects.
[pscustomobject] @{ Name="array object $count" }
}
# Apply the workaround.
$testlist | Out-String
# Use implicit output again.
"test output 6"
"test output 7"
throw "pretend error"
[1] That is, throw
generates a runspace-terminating (script-terminating) error, which instantly aborts overall execution - unlike a statement-terminating error, which only aborts the statement at hand, such as would occur when an exception occurs in a .NET method call; You can convert all statement-terminating as well as all non-terminating errors to runspace-terminating ones by setting the $ErrorActionPreference
preference variable to 'Stop'
- see this answer.