Powershell executable isn’t outputting to STDOUT

As Ansgar’s comment implies: On Windows, Start-Process runs console programs in a new console window by default, asynchronously.

If that program completes quickly, you may see the new console window flash only briefly, as it opens and closes soon thereafter, or you may miss the flash altogether – either way, its output will not show in the caller’s console window.

Adding -Wait to the Start-Process call would make the invocation synchronous, and adding -NoNewWindow would make it run in the same console, yet the calling PowerShell session wouldn’t be able to capture or redirect the invoked program’s output – see below.

Taking a step back: Do NOT use Start-Process[1] if you want to run a console program synchronously, with its standard streams connected to PowerShell’s streams – just invoke such a program directly:

packages/mdoc/tools/mdoc.exe --version

If the external program’s path / name must be quoted (because its path contains spaces) and/or it is stored in a variable, simply use &, the call operator, to invoke it:

# Via a quoted string:
& "packages/mdoc/tools/mdoc.exe" --version

# Via a variable:
$exePath = "packages/mdoc/tools/mdoc.exe"
& $exePath --version

Using the direct-invocation approach gives you synchronous execution for free, as well as the ability to capture and/or redirect the invoked program’s stdout and stderr streams.


To put it all together (based on your later comments):

nuget install mdoc -OutputDirectory packages -ExcludeVersion

$exePath = "packages/mdoc/tools/mdoc.exe"

& $exePath --version

"done"

This prints the version number – mdoc 5.7.2 as of this writing – just before printing done (verified on Windows PowerShell v5.1.17134.48 on Microsoft Windows 10 Pro (64-bit; Version 1709, OS Build: 16299.371)).


Optional reading: capturing stdout / stderr output from external programs:

To capture stdout output, simply assign the call to a variable:

$version = & $exePath --version  # $version receives stdout output as an *array of lines*

$version receives either a string scalar (single string) if there was only 1 line of output, or an array of strings representing the output lines.

To also capture stderr output, use redirection 2>&1:

[string[]] $allOutput = & $exePath --version 2>&1

Note the cast to [string[]], which ensures that the stderr lines are captured as strings too.

  • By default, they are captured as [System.Management.Automation.ErrorRecord] instances, which in Windows PowerShell will somewhat confusingly print them as if they were PowerShell errors – this problem has been fixed in PowerShell Core.

  • Conversely, however, if you don’t convert the type of the elements of the array that is returned to strings, you can examine each element with -is [System.Management.Automation.ErrorRecord] to determine whether it originated from stdout or stderr.

Note: When PowerShell communicates with external programs, character-encoding issues come into play: see this answer for details.


[1] Or the underlying .NET API, System.Diagnostics.Process.
For guidance on when Start-Process is or isn’t appropriate, see GitHub docs issue #6239

Leave a Comment