2

A trap invoked in a function called from inside of loop is not invoked. A trap works as expected if invoked in loop body directly. Same function, if called outside of loop, invokes trap.

#!/bin/bash

function error_handler() #---------------------
{
    echo "ERROR STATUS: $?"
    echo "ERROR COMMAND: $BASH_COMMAND"
    echo "ERROR STACK:"
    caller
    echo "---------- end of stack dump ----------"
    exit 1
}

function func()
{
    echo "..inside func( $1)"
    if false
    then
        true
        rv=0
    else
        false
        rv=1
    fi
    echo "..returning from func( $1), rv=$rv"
    return $rv
}

set -o errtrace -o errexit
trap error_handler ERR

for N in 1 2 ; do

    echo -e "\nbegin loop $N"
    if func $N
    then
        echo "result of func($N): success"
    else
        echo "result of func($N): failed"
    fi
    echo "loop $N is done"
    false

done

func 1
func 2

Actual result running script above:

begin loop 1
..inside func( 1)
..returning from func( 1), rv=1
result of func(1): failed
loop 1 is done
ERROR STATUS: 1
ERROR COMMAND: false
ERROR STACK:
41 ./a.sh
---------- end of stack dump ----------

but I expect trap from the false line inside func(), not the false at the end of the program. Let's comment out false at the end. Result is:

begin loop 1
..inside func( 1)
..returning from func( 1), rv=1
result of func(1): failed
loop 1 is done

begin loop 2
..inside func( 2)
..returning from func( 2), rv=1
result of func(2): failed
loop 2 is done
..inside func( 1)
ERROR STATUS: 1
ERROR COMMAND: false
ERROR STACK:
21 ./a.sh
---------- end of stack dump ----------

A trap was invoked inside func() now, but not in loop 1! Both loop 1 and loop 2 are completed without trap. It was func 1 after the function returns, which invoked a trap. Too late.

Why?

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Vlado B.
  • 75
  • 4

1 Answers1

2

It's not looping that's the culprit, but rather the if statement here:

if func $N

When you use func in the test in an if statement, ERR traps are suspended for the entire duration of the test. Errors that occur inside func() do not trigger ERR traps, nor do they cause the shell to exit if errexit is enabled.

Quoting the bash man page:

The ERR trap is not executed if the failed command is part of the command list immediately following a while or until keyword, part of the test in an if statement, part of a 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 value is being inverted using !. These are the same conditions obeyed by the errexit (-e) option.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
  • I read the manual page, but there is not obvious that traps are suspended for nested commands too. Thanks for clarification. I can rewrite my example code. I can replace line ``if func $N`` with ``func $N ; if [[ $? -eq 0 ]]``, but how to achieve enabled traps inside function ``func``? Is there only one way: moving ``func`` to separate script ``func.sh``? Putting another ``set -o errtrace -o errexit ; trap error_handler ERR`` to beginning of function ``func`` doesn't help. – Vlado B. Apr 12 '19 at 12:38
  • I agree, it's not obvious. I only recently learned exactly what it means to be "part of the test", that error checking is globally suspended until the test completes. `func $N; if ...` is sufficient to get traps enabled in `func`. – John Kugelman Apr 12 '19 at 13:16