Bash 循环调用的函数中未调用陷阱

Bash trap is not invoked in function called from loop

在从循环内部调用的函数中调用的陷阱不会被调用。如果直接在循环体中调用,陷阱将按预期工作。相同的函数,如果在循环外调用,会调用 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( )"
    if false
    then
        true
        rv=0
    else
        false
        rv=1
    fi
    echo "..returning from func( ), 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

实际结果 运行 上面的脚本:

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 ----------

但我希望陷阱来自 func() 内的 false 行,而不是程序末尾的 false。 我们在最后注释掉false。结果是:

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 ----------

现在 func() 内部调用了一个陷阱,但不是在循环 1 中! loop 1 和 loop 2 都是在没有 trap 的情况下完成的。它是 func 1 在调用陷阱的函数 returns 之后。太晚了。

为什么?

罪魁祸首不是循环,而是这里的 if 语句:

if func $N

当您在 if 语句的测试中使用 func 时,ERR 陷阱会在整个测试期间暂停。 func() 内发生的错误不会触发 ERR 陷阱,如果启用 errexit,它们也不会导致 shell 退出。

引用 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.