Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Powershell executable isn't outputting to STDOUT

Tags:

powershell

From a powershell script (with nuget installed and on the path), I'm trying to execute an executable (.net, if that matters) ... but for some reason, I can't get the STDOUT to display in the command window.

nuget install mdoc -OutputDirectory packages -ExcludeVersion

start-process "packages/mdoc/tools/mdoc.exe" "--version"

echo "done"

This should output mdoc 5.7.2 (at the time of this post, current version). But you'll see the nuget output, then done.

Any thoughts on why this is not showing up?

like image 402
Joel Martinez Avatar asked Jan 02 '23 03:01

Joel Martinez


1 Answers

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 by default
  • the ability to capture and/or redirect the invoked program's stdout and stderr streams.
  • access to the program's process exit code is via PowerShell's automatic $LASTEXITCODE variable.

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.

    • This answer uses this approach to separate stdout lines from stderr lines after merging with 2>&1, so that they can be saved to separate files (which with > in Windows PowerShell requires explicitly stringifying the [System.Management.Automation.ErrorRecord] instances).

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

like image 73
mklement0 Avatar answered Jan 14 '23 00:01

mklement0