PowerShell exits with exit code 1 rather than returning $LASTEXITCODE when last process exit code is >0

Created on 31 Dec 2019  Â·  6Comments  Â·  Source: PowerShell/PowerShell

When running commands via pwsh -c '<command>', PowerShell will return an exit code of 1 in the event that <command> returns an exit code greater than 0. This is particularly problematic for tools like https://github.com/dense-analysis/ale/, which rely on an accurate exit code from the command which they spawn (a linter or fixer in this case).

Steps to reproduce

pwsh -c "bash -c 'exit 2'"; echo $LASTEXITCODE

Expected behavior

The exit code of the native application should be returned.

2

Actual behavior

PowerShell returns a generic "failure" exit code of 1.

1

Environment data

Name                           Value
----                           -----
PSVersion                      7.0.0-rc.1
PSEdition                      Core
GitCommitId                    7.0.0-rc.1
OS                             Darwin 18.7.0 Darwin Kernel Version 18.7.0: Tue Aug 20 16:57:14 PDT 201…
Platform                       Unix
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0, 5.0, 5.1.10032.0, 6.0.0, 6.1.0, 6.2.0, 7.0.0-rc.1}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0
Issue-Enhancement Resolution-Answered Resolution-By Design WG-Engine

Most helpful comment

@TravisEz13

I find reiterating an already-mentioned _workaround_ for the problematic behavior as the solution and dismissing a request to _fix it_ with a curt "this is expected" and "would break existing scripts" disappointing, and I suspect I'm not the only one.

I invite you to provide a more nuanced response at #13501

All 6 comments

In case you don't know this workaround, using the -File parameter will return the actual exit code:

$ cat exit2.sh
#!/bin/sh

exit 2

$ pwsh -nop -noni -f exit2.sh || echo $?
2

Thank you for that; that's definitely a useful workaround, which I'll add to my repertoire. I must say, though, that having to generate and execute a temporary script just to get the expected exit code from the shell is... less than ideal. Honestly, if I tried to put that in a PR for ALE I expect it would be rejected immediately.

One code smell that I see very regularly is the amount of calls to cmd /c out there in build scripts and "cross-platform" tools which have special cases for running on Windows. When I see an issue like this, however, I'm stuck having to agree that cmd is actually the better option, which is sad given that it hasn't been touched for what... 20 years now?

It also makes it harder to break out of cases like https://github.com/dense-analysis/ale/blob/5e69aaf4c2b4ffbfd4fbe0af820e23bf559b8e14/autoload/ale.vim#L223 where functionality is based on the OS rather than the shell, which is becoming more important now that PowerShell runs on Linux and macOS (my current OS at work).

GitHub
Check syntax in Vim asynchronously and fix files, with Language Server Protocol (LSP) support - dense-analysis/ale

I agree that the behavior is problematic.

A more direct workaround is to append ; exit $LASTEXITCODE to the -Command argument:

pwsh -c "bash -c 'exit 2'; exit `$LASTEXITCODE"; echo $LASTEXITCODE

Note that executing _PowerShell_ scripts (*.ps1) with -File behaves differently from POSIX-like shell scripts:

In the _absence_ of an explicit exit statement, POSIX-like shells report the
_last statement_'s exit code as the script's, whereas *.ps1 scripts _default to 0_.

  • Calling a *.ps1 script from _inside_ PowerShell behaves differently again: $LASTEXITCODE after a script invocation reflects whatever _external program_ was last called in the absence of exit <n>, not implicitly 0 - see #11712

Summarizing the behavior of _PowerShell code_ with the CLI:

-Command / -c behaves _almost_ like a POSIX-like shell in that it automatically uses the _last_ statement's success status, but discards the specific exit code in favor of mapping the abstract Boolean $? to 0 or 1 (the behavior that prompted creation of this issue).

-File with a *.ps1 files behaves differently from -Command / -c and POSIX-like shells in that only _explicit_ exit calls matter, whereas calling *.ps1 files PowerShell-internally behaves differently yet again.

This is expected and changing it would break existing scripts

This should get the expected behavior

powershell

pwsh -c "bash -c 'exit 2'; exit `$LASTEXITCODE"; echo $LASTEXITCODE

This issue has been marked as answered and has not had any activity for 1 day. It has been closed for housekeeping purposes.

@TravisEz13

I find reiterating an already-mentioned _workaround_ for the problematic behavior as the solution and dismissing a request to _fix it_ with a curt "this is expected" and "would break existing scripts" disappointing, and I suspect I'm not the only one.

I invite you to provide a more nuanced response at #13501

Was this page helpful?
0 / 5 - 0 ratings