9

Is it possible to propagate an exit code to the caller in case of a syntax error in a Bash script with an EXIT trap? For example, if I have:

#! /bin/bash

set -eu

trap "echo dying!!" EXIT

echo yeah
echo $UNBOUND_VARIABLE
echo boo

Then, running it gives an exit code 0 even if the script did not really end successfully:

$ bash test.sh
yeah
test.sh: line 8: UNBOUND_VARIABLE: unbound variable
dying!!

$ echo $?
0

But if I comment out the exit trap, the script returns 1. Alternatively, if I replace the line with the unbound variable with a command that returns nonzero (e.g. /bin/false), that exit value is propagated as I would like it to.

Jolta
  • 2,620
  • 1
  • 29
  • 42
Eemeli Kantola
  • 5,437
  • 6
  • 35
  • 43

2 Answers2

7

The shell exits with the result of the last executed command. In your trap case, that's echo, which usually returns with success.

To propagate your value, simply exit with it.

#!/bin/bash

set -eu

die() {
  echo "Dying!!"
  exit "$1"
}

trap 'die $?' EXIT

echo yeah
echo $unbound
echo boo

Also note that set -e is considered harmful -- it makes you think the script will exit if a command fails, which it won't always do.

that other guy
  • 116,971
  • 11
  • 170
  • 194
  • 3
    Is your `-e` comment just about people not understanding what a "simple command" is or is there some other gotcha I'm not aware of here? – Etan Reisner Jul 31 '14 at 17:20
  • This solution doesn't work but also returns an exit code 0. And in case I replace `echo $UNBOUND_VARIABLE` with `false` in the original version, the script exits properly with a code 1 and not echo's 0. – Eemeli Kantola Jul 31 '14 at 17:25
  • 2
    @EtanReisner many gotchas. `myfunction() { set -e; false; echo "You would think this wouldn't execute"; }; myfunction && echo oops` is one – that other guy Jul 31 '14 at 17:41
  • 1
    @EemeliKantola can you run `cat script; ./script; echo $?` and show the output? – that other guy Jul 31 '14 at 17:46
  • And also, looks like I have: `GNU bash, version 3.2.51(1)-release (x86_64-apple-darwin13)` – Eemeli Kantola Jul 31 '14 at 18:02
  • Ok, so with `GNU bash, version 4.2.25(1)-release (x86_64-pc-linux-gnu)` the thing returns 1 as it should. Great... – Eemeli Kantola Jul 31 '14 at 18:04
  • Actually the original piece of script does exit with 1 on this newer Bash as well. – Eemeli Kantola Jul 31 '14 at 18:11
  • @EemeliKantola huh. You don't happen to have a trap on DEBUG in your shell, do you? – that other guy Jul 31 '14 at 18:40
  • 4
    The problem is that older versions of `bash` (3.2 for sure, possibly early 4.x as well) do not appear set `$?` before the trap executes. `echo yeah` runs fine, so `$?`is 0, but the next `echo` doesn't even run because of the unbound variable. So in some sense, the last command to run exited 0. Later versions of `bash` seem to set `$?` to 1 for the shell itself before the trap executes. – chepner Jul 31 '14 at 19:18
  • Presumably that `myfunction` gotcha is because the function contents are not treated as simple commands but a single block? Which, I'll admit, certainly puzzles me. – Etan Reisner Jul 31 '14 at 21:03
  • 1
    @EtanReisner `myfunction` by itself works fine, but `&&` disables the exit behavior for the left and right side. It's scary that a 100% correct function can become incorrect and possibly dangerous just by e.g. invoking it in an if statement. – that other guy Jul 31 '14 at 22:35
  • Ah. Of course. (I thought I'd tested that but missed it somehow.) That makes perfect sense but absolutely does compose badly. – Etan Reisner Jul 31 '14 at 23:28
3

This behavior is related to different Bash versions. The original script works as expected on Bash 4.2 but not on 3.2. Having the error-prone code in a separate script file and running it in a subshell works around problems in earlier Bash versions:

#!/bin/bash

$BASH sub.sh
RETVAL=$?

if [[ "$RETVAL" != "0" ]]; then
  echo "Dying!! Exit code: $RETVAL"
fi

sub.sh:

set -eu

echo yeah
echo $UNBOUND_VARIABLE
echo boo
Eemeli Kantola
  • 5,437
  • 6
  • 35
  • 43