15

How can I make Powershell behave like Bash with its flag set -e? set -o errexit makes a Bash script "Exit immediately if a simple command exits with a non-zero status".

I thought I could do this by setting $ErrorActionPreference="Stop" but this doesn't seem to work. Suppose I have a script a.ps1

$ErrorActionPreference="Stop"
& cmd /c "exit 1"
echo "cmd exited `$LastExitCode=$LastExitCode and `$?=$?"

If I run it

.\a. ; echo "a.ps1 exited `$LastExitCode=$LastExitCode `$?=$?"

To my surprises it prints

cmd exited $LastExitCode=1 and $?=False
a.ps1 exited $LastExitCode=1 $?=True

What's going on?! I expected a.ps1 to exit after the first line, throwing an error and setting $? to False.

Is there any official documentation explaining $ErrorActionPreference? All I found was this post on a blog about coffee.

MarcH
  • 18,738
  • 1
  • 30
  • 25
Colonel Panic
  • 132,665
  • 89
  • 401
  • 465
  • Note `set -e / set -o errexit` is useful because it can catch many errors, however it will also miss many others: https://stackoverflow.com/questions/4072984/how-do-i-get-effect-and-usefuless-of-set-e-inside-a-shell-function – MarcH Nov 14 '18 at 17:36
  • I believe `$ErrorActionPreference` is checking for whether the child command wrote to StdErr (Write-Error) or not. It's not checking the exit code. A simple test confirms this. See a bit more discussion here: [link](https://github.com/PowerShell/PowerShell/issues/3415) – orion elenzil Nov 04 '19 at 17:06
  • For anyone who wants to fix this for native programs the approved RFC is here https://github.com/PowerShell/PowerShell-RFC/pull/277 last comment April 1st 2021 they are looking for an experimental implementation – b-rad15 Jun 29 '21 at 06:53

1 Answers1

14

$ErrorActionPreference works as intended, it's just that exit codes from native programs are not nearly as well-behaved as PowerShell cmdlets.

For a cmdlet the error condition is fairly easy to determine: Did an exception happen or not? So the following would not reach the Write-Host statement with $ErrorActionPreference set to Stop:

Write-Error blah

or

Get-ChildItem somepathwhichsurelydoesntexisthere

For native programs an exit code of zero usually signals that no error occurred but that's merely a convention, as is a non-zero exit code signalling an error. Would you say that if choice exists with an exit code of 1 that it was an error?

The only robust way to handle this is to check the exit code after running a native command and handling it yourself, if needed. PowerShell doesn't try to guess what the result meant because the conventions aren't strong enough to warrant a default behaviour in this case.

You can use a function if you want to make your life easier:

function Invoke-NativeCommand {
  $command = $args[0]
  $arguments = $args[1..($args.Length)]
  & $command @arguments
  if ($LastExitCode -ne 0) {
    Write-Error "Exit code $LastExitCode while running $command $arguments"
  }
}

But in general many programs need different handling because they don't adhere to said convention.

Raven
  • 2,951
  • 2
  • 26
  • 42
Joey
  • 344,408
  • 85
  • 689
  • 683
  • 3
    Shucks, this is frustrating. Thanks for the function, I'll use that. – Colonel Panic Jul 12 '12 at 13:54
  • 3
    I do understand that there are programs which might misbehave and that it's important to have some flexibility because of it, but in practice, almost all the command line tools I have called from a PowerShell script thus follow this convention well. Granted, most of my experience is with build scripts, but I do think that the convention is a lot stronger than this answer makes it sound. I think it's strong enough to warrant something built in, even if it's just a function like the one here so we don't have to redefine it in all our scripts/packages. – jpmc26 Aug 20 '14 at 20:45