Our build system uses pwsh (Powershell 7.4.0) to execute a load of commands to build and test our software. We recently encountered a problem where something like the following example launched our test harness in a new window resulting in our tests appearing to pass instead of fail. Why is there a difference between these two and how could we harden things (other than removing the space)?
This one silently failes as it appear to launch the test
PS C:\Temp>& "${Env:NUNIT_LOCATION}\nunit-console-x86.exe " CodeUnderTest.dll
PS C:\Temp>$LastExitCode
0
This one works as expected and reports failures on the command line (and modifies the return code):
PS C:\Temp>& "${Env:NUNIT_LOCATION}\nunit-console-x86.exe" CodeUnderTest.dll
<test error outputs here>
PS C:\Temp>$LastExitCode
35
You're seeing what is arguably a bug, present in both Windows PowerShell (the legacy, ships-with-Windows, Windows-only edition of PowerShell whose latest and last version is 5.1) and PowerShell (Core) 7 (the modern, cross-platform, install-on-demand edition) as of 7.5.0:[1]
With the trailing space in the path, PowerShell doesn't recognize the executable as an executable (application).
Instead, it treats it like a document and therefore performs the equivalent of a Start-Process call[2] in order to delegate the invocation to the Windows (GUI) shell, which has two implications:
The console application you're trying to launch opens in a new console window that automatically closes when the application exits.
It launches asynchronously, and therefore its exit code is not captured in the automatic $LASTEXITCODE variable, so whatever $LASTEXITCODE was previously in effect remains in effect.
how could we harden things
Short of writing a generic wrapper function that trims trailing spaces from the executable path, you can perform trimming ad hoc; e.g.:
$exePath = "${Env:NUNIT_LOCATION}\nunit-console-x86.exe "
& $exePath.Trim() CodeUnderTest.dll
[1] The bug has been reported in GitHub issue #24990.
[2] Start-Process can be made to act synchronously with the -Wait switch, and the -PassThru switch can be used to make the call output a System.Diagnostics.Process instance whose .ExitCode property can then be queried, but note that, except in unusual scenarios, there is no good reason to use Start-Process for executing console applications, not least because you won't be able to directly capture their output. See this answer for more information.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With