Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bash exit status not caught despite set -e and/or trap being active

Tags:

bash

Can someone explain the bash/set -e behaviour on the code snippet below ?

#!/bin/bash

# Comment if you want to test the trap only
set -e -o pipefail -u -E

# Comment if you want to test the set -e only
trap "echo ERROR CAUGHT; exit 12" ERR

function reproduce() {
    # Trigger arithmetic error on purpose
    a=$((1109962735 - hello=12272 + 1))
}

reproduce

# The script is expected to trigger the trap and/or activate the set -e. In both cases it should stop and exit here on error.

status_code=$?
echo "STATUS ${status_code}"
if [[ "${status_code}" != "0" ]];then
    echo "FIXME: why was status code not caught by set -e ?"
    echo "Executing false to prove set -e is still active"
    false
    # If the following is not executed then it proves -e is still active
    echo "set -e not active !!!!!"
    exit 2
fi

Here is what is obtained when executing it:

$ bash reproduce.sh
reproduce.sh: line 8: 1109962735 - hello=12272 + 1: attempted assignment to non-variable (error token is "=12272 + 1")
STATUS 1
FIXME: why was status code it not caught by set -e ?
Executing false to prove set -e is still active
ERROR CAUGHT

Checking the exit code

$ echo $?
1

Bash version

bash --version
GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)

Reproduced as well with

GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)

Additional notes, related to comments (thanks to all suggestions anyway):

  • Commenting trap does not change the weird behaviour observed
  • Removing set -e to only keep the trap does trigger the trap either
like image 259
raphael.glon Avatar asked Nov 06 '19 10:11

raphael.glon


2 Answers

Let's simplify it; minimum amount of code required for reproducing the issue you're dealing with is

set -e
: $((+)) # arithmetic expansion error
echo survived

According to the standard, this should never print survived, it says a POSIX shell running non-interactively shall immediately exit upon an expansion error. But seemingly Bash doesn't think so. Although this difference isn't explicitly documented in the man page, in description of POSIX mode it says

  1. Non-interactive shells exit if a syntax error in an arithmetic expansion results in an invalid expression.

We can say this means in its default operating mode, a non-interactive Bash session doesn't exit upon such error, but as you realized, it doesn't trigger errexit mechanism, or ERR trap either. Instead, it assigns a non-zero value to $? an moves on.

To overcome this and get the expected behavior, you should define reproduce as follows

function reproduce() (
    # Trigger arithmetic error on purpose
    a=$((1109962735 - hello=12272 + 1))
)

This way the expansion error will take place in a subshell and cause it to exit with a non-zero status, thus, errexit and trap will be able to catch it.


Upon dash-o's request, here is a version that sets a for the current execution environment when the expression is valid

function reproduce() {
    if : $((expression)); then
        a=$((expression))
    fi
}
like image 100
oguz ismail Avatar answered Sep 28 '22 05:09

oguz ismail


On surface, it looks as if bash will not trigger the trap on various SYNTAX errors. Only when it a command (external, built-in) is executed (and return non-zero), the ERR trap will be trigered.

From the man page:

If a sigspec is ERR, the command arg is executed whenever a pipeline (which may consist of a single simple command), a list, or a compound command returns a non-zero exit status, subject to the following conditions ...

The ERR trap only applies to PIPELINE. If bash identifies a syntax error, it aborts before executing the pipeline, therefore NO trap. Even though he documentation for '-e' specify the same condition (if a pipeline ... exit with non-zero status), the observed behavior is different.

If you try other expansions - e.g. command expansion- trap is triggered, as there is pipeline execution:

  • a=$(bad-commands)
  • a=$([)

If use try various syntax errors in arithmetic expansion, trap not triggered - there was no pipeline.

  • a=$((2+))
  • a=$((2 @))

Also, other bash Syntax error do not trigger the trap: (), [[ ]].

I could not find a solution that does not require extensive changes to the source script. May be file a bug/feature request with the bash team ?

like image 36
dash-o Avatar answered Sep 28 '22 07:09

dash-o