5

Can I assign a different exit status to a shell script in a trap handler?

Through trial-and-error, I found that calling exit in trap the exit status can be changed. Normal commands, whether fail or succeed, won't change the exit status.

Now I want to know, if I can rely on this behavior, or is it some implementation quirk?

I have been trying to find some doc on this, but to no avail.

Test script:

#!/bin/bash
function handler {
    # a successful command won't change script exit status
    echo handler, status=$1
    # badcommand won't change script exit status
    #badcommand
    # exit will change script exit status
    #exit 23
}
trap 'handler $?' EXIT
#badcommand

Usage:

./trap_test.sh; echo status_out=$?
1
  • 1
    Apparently if you use set -e in your script, then errors in the trap will be returned to the caller. For example, try this command with and without the -e part: bash -e -c "trap 'false' EXIT"; echo $? Commented Oct 6, 2023 at 16:30

2 Answers 2

8

I found that [by] calling exit in trap the exit status can be changed. Normal commands, whether fail or succeed, won't change the exit status. Now I want to know, if I can rely on this behavior, or is it some implementation quirk?

Yes you can rely on it, and it is documented, in the shell standard:

NAME
exit - cause the shell to exit
...
DESCRIPTION
...
A trap on EXIT shall be executed before the shell terminates, except when the exit utility is invoked in that trap itself, in which case the shell shall exit immediately.
...
EXIT STATUS
The exit status shall be n, if specified, except that the behavior is unspecified if n is not an unsigned decimal integer or is greater than 255. Otherwise, the value shall be the exit value of the last command executed, or zero if no command was executed. When exit is executed in a trap action, the last command is considered to be the command that executed immediately preceding the trap action.

(same thing about return)

And when there's no exit command but an exit trap was set, the following should apply:

Unless otherwise stated, the exit status of a command shall be that of the last simple command executed by the command.

and all the rules about compound commands, sequential lists, etc which make no special exception for the existence of an EXIT trap.

2
  • A caveat is needed in the case when set -e has been used in the script. For example, try this command with and without the -e flag: bash -e -c "trap 'false' EXIT"; echo $?. If -e has been used, then the error code from the trap IS RETURNED to the caller. (I was surprised to discover this, since I agree with your interpretation of the shell standard.) Commented Oct 6, 2023 at 16:27
  • @superbatfish using -e causes the shell to exit immediately if a command exits with a non-zero status, as you likely know. So your command with -e could be rewritten without it, as bash -c "trap 'true; exit 1' EXIT"; echo $?, and the behaviour is the same. So I think this is consistent with the above -- it can be seen as a case of exit being executed within the exit trap . Commented Jun 12 at 16:09
1

I don't see the man page mentioning it, so other than looking at the code, experimentation seems like the best way of figuring out what happens.

It makes sense to me though that an EXIT trap wouldn't always change the exit status: a script might want to do some cleanup via the trap on exit, but not have that cleanup mess the exit status set by the main script. And, as you said, the trap can just run exit again to set a desired value.

E.g. if you have this:

tmpfile=$(mktemp)
trap 'rm -f "$tmpfile"' EXIT
# something
exit 1

the rm would likely reset the exit status to zero, which doesn't seem that useful.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .