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.
在从循环内部调用的函数中调用的陷阱不会被调用。如果直接在循环体中调用,陷阱将按预期工作。相同的函数,如果在循环外调用,会调用 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 awhile
oruntil
keyword, part of the test in anif
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 theerrexit
(-e
) option.