You're getting both the INT and ERR signals; SIGINT is handed to sleep
, who exits with a non-zero return code. The non-zero return code then triggers the trap for SIGERR.
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...
An example, to see the separate traps:
set -ex -o pipefail
trap "echo Clean up for INT" INT
trap "echo Clean up for ERR" ERR
sleep 9999
Executing, then Control-C:
+ trap 'echo Clean up for INT' INT
+ trap 'echo Clean up for ERR' ERR
+ sleep 9999
^C++ echo Clean up for INT
Clean up for INT
++ echo Clean up for ERR
Clean up for ERR
As for calling the trap only once, one option would be to reset the ERR
trap while inside the INT
trap:
...
trap "echo Clean up for INT; trap ERR" INT ERR
...
... which results in:
+ trap 'echo Clean up for INT; trap ERR' INT ERR
+ sleep 9999
^C++ echo Clean up for INT
Clean up for INT
++ trap ERR
dash
/sh
,EXIT
won't be trapped on e.g.SIGTERM
. – Berkant İpek Mar 15 '21 at 12:49