Reputation: 52321
In a Bash script, I activate a trap, and later deactivate it by calling trap - EXIT ERR SIGHUP SIGINT SIGTERM
. When I do the deactivation directly in the script, it works. However, when I put the exact same line of code in a Bash function, it is ignored, i.e. the trap is still activated if, later, a command returns an exit code different from zero. Why?
I have a bunch of functions to work with traps:
trap_stop()
{
echo "trap_stop"
trap - EXIT ERR SIGHUP SIGINT SIGTERM
}
trap_terminate()
{
local exitCode="$?"
echo "trap_terminate"
trap_stop
local file="${BASH_SOURCE[1]}"
local stack=$(caller)
local line="${stack% *}"
if [ $exitCode == 0 ]; then
echo "Finished."
else
echo "The initialization failed with code $exitCode in $file:${line}."
fi
exit $exitCode
}
trap_start()
{
echo "trap_start"
trap "trap_terminate $LINENO" EXIT ERR SIGHUP SIGINT SIGTERM
}
When used like this:
trap_start # <- Trap started.
echo "Stopping traps."
trap_stop # <- Trap stopped before calling a command which exits with exit code 2.
echo "Performing a command which will fail."
ls /tmp/missing
exit_code="$?"
echo "The result of the check is $exit_code."
I get the following output:
trap_start Stopping traps. trap_stop Performing a command which will fail. ls: cannot access '/tmp/missing': No such file or directory trap_terminate trap_stop The initialization failed with code 2 in ./init:41.
Despite the fact that function deactivating the trap was called, the trap was still triggered when calling ls
on a directory which doesn't exist.
On the other hand, when I replace the call to trap_stop
by the actual trap -
statement, like this:
trap_start
echo "Stopping traps."
trap - EXIT ERR SIGHUP SIGINT SIGTERM # <- This statement replaced the call to `trap_stop`.
echo "Performing a command which will fail."
ls /tmp/missing
exit_code="$?"
echo "The result of the check is $exit_code."
then the output is correct, i.e. the trap is not activated and I reach the end of the script.
trap_start Stopping traps. Performing a command which will fail. ls: cannot access '/tmp/missing': No such file or directory The result of the check is 2.
Why is moving trap -
to a function makes it stop working?
Upvotes: 3
Views: 2146
Reputation: 6048
EDIT (courtesy of @KamilCuk): If your bash is older than 4.4, upgrade your bash, it could solve the problem.
I added some debugging to your code:
echo "Stopping traps."
trap -p
trap_stop # <- Trap stopped before calling a command which exits with exit code 2.
trap -p
And got:
Stopping traps.
trap -- 'trap_terminate 29' EXIT
trap -- 'trap_terminate 29' SIGHUP
trap -- 'trap_terminate 29' SIGINT
trap -- '' SIGFPE
trap -- 'trap_terminate 29' SIGTERM
trap -- '' SIGXFSZ
trap -- '' SIGPWR
trap -- 'trap_terminate 29' ERR
trap_stop
trap -- '' SIGFPE
trap -- '' SIGXFSZ
trap -- '' SIGPWR
trap -- 'trap_terminate 29' ERR
As you can see, the trap -
part does work, except for the ERR
condition.
After some man page time:
echo "Stopping traps."
set -E
trap_stop # <- Trap stopped before calling a command which exits with exit code 2.
yields:
trap_start
Stopping traps.
trap_stop
Performing a command which will fail.
ls: cannot access '/tmp/missing': No such file or directory
The result of the check is 2.
The relevant part of bash(1)
:
-E If set, any trap on ERR is inherited by shell functions, command substitutions, and commands executed in a subshell environment. The ERR trap is normally not inherited in such cases.
That said, this seems to be a bug in bash:
#!/bin/bash
t1()
{
trap 'echo t1' ERR
}
t2()
{
trap 'echo t2' ERR
}
t1
false
t2
false
yields:
t1
t1
whereas I'd expect at the very least:
t1
t2
Upvotes: 4