1

Using trap may help in writing cleanest bash script. However I would like to know whether a solution exists in order to trap an error in the following case:

GNU bash, version 4.2.45

case OK

#!/bin/bash
trap 'echo OK i see error at line $LINENO;exit' ERR 
unknowncommand
echo "test KO should never reach me"

case KO

#!/bin/bash
trap 'echo OK i see error at line $LINENO;exit' ERR
unknowncommand && echo "miss the trap"
echo "test KO should never reach me"
  • Unfortunately we reach the last sentence as if && make the whole sentence not an ERR.
Jolta
  • 2,620
  • 1
  • 29
  • 42
chris
  • 83
  • 6
  • 1
    http://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html Exit immediately if a pipeline [...] returns a non-zero status. The shell does not exit if the command that fails is part of the command list immediately following a while or until keyword, part of the test in an if statement, part of any command executed in a && or || list except the command following the final && or ||, any command in a pipeline but the last, or if the command’s return status is being inverted with !. – chris Apr 18 '14 at 13:42
  • More relevant (but basically the same idea) is the description of the `ERR` psuedosignal in the documentation for the `trap` builtin. – chepner Apr 18 '14 at 13:57
  • @chris Confusing, but accurate. Bash seems to interpret Booleans as a command list even without using braces to form a list expression. While my answer is correct, your pointer to `-e` helps to explain how Bash is parsing the expression in question. – Todd A. Jacobs Apr 18 '14 at 14:12

2 Answers2

6

You want to evaluate the exit status of the whole evaluation, rather than attempting a short-circuit. One way to do this is to wrap the entire thing in a subshell. For example:

#!/bin/bash
trap 'echo "OK i see error at line $LINENO"; exit' ERR
(unknowncommand && echo "miss the trap")
echo "test KO should never reach me"

This works as expected. The result looks like this:

$ /tmp/err.sh 
/tmp/err.sh: line 3: unknowncommand: command not found
OK i see error at line 3
Todd A. Jacobs
  • 81,402
  • 15
  • 141
  • 199
  • This (or at least the first part) isn't quite right. The documentation for `ERR` simply says that the handler is not run if the failed command is, among other things, part of a list (commands joined by `&&` or `||`). Wrapping the list in a subshell causes `ERR` to be triggered by the subshell returning non-zero because of the non-zero status of the list. – chepner Apr 18 '14 at 13:59
  • @chepner I can't help what your shell is outputting for `help trap`. What I posted is indeed what Bash 4.2.45 prints on my system. One supposes your mileage may vary. – Todd A. Jacobs Apr 18 '14 at 14:03
  • That's the return status of the `trap` command itself, not the handler that is fired for the `ERR` signal. `unknowncommand` still has a non-zero exit status (and as a result the subshell does as well). The difference is that the `ERR` handler is not fired for a command list, but is (apparently, although not explicitly stated in the man page) for a subshell. – chepner Apr 18 '14 at 14:05
1

According to the documentation for 4.2 (recent at the time of this answer)

If a sigspec is ERR, the command arg is executed whenever a simple command has a non-zero exit status, subject to the following conditions. The ERR trap is not executed if the failed command is part of the command list immediately following an until or while keyword, part of the test following the if or elif reserved words, part of a command executed in a && or || list, or if the command’s return status is being inverted using !.

One way to work around this is to use an if statement to check the value of $? instead of an && list.

trap 'echo OK i see error at line $LINENO;exit' ERR
unknowncommand
if [ $? -eq 0 ]; then
    echo "miss the trap"
fi
echo "test KO should never reach me"

Another that seems to work is to wrap the command in a subshell, although the documentation does not seem to mention that ERR is fired for a failed subshell. (A subshell is not generally considered a simple command, but the documentation doesn't deny it, either.) The exit status of a subshell is the exit status of the last line, which in this case (x && y) is the exit status of x if it fails, or else the exit status of y.

trap 'echo OK i see error at line $LINENO;exit' ERR
( unknowncommand && echo "miss the trap" )
echo "test KO should never reach me"
chepner
  • 497,756
  • 71
  • 530
  • 681