68

There're many exit points in my bash code. I need to do some clean up work on exit, so I used trap to add a callback for exit like this:

trap "mycleanup" EXIT

The problem is there're different exit codes, I need to do corresponding cleanup works. Can I get exit code in mycleanup?

Jolta
  • 2,620
  • 1
  • 29
  • 42
Dagang
  • 24,586
  • 26
  • 88
  • 133

4 Answers4

84

The accepted answer is basically correct, I just want to clarify things.

The following example works well:

#!/bin/bash

cleanup() {
    rv=$?
    rm -rf "$tmpdir"
    exit $rv
}

tmpdir="$(mktemp)"
trap "cleanup" EXIT
# Do things...

But you have to be more careful if doing cleanup inline, without a function. For example this won't work:

trap "rv=$?; rm -rf $tmpdir; exit $rv" EXIT

Instead you have to escape the $rv and $? variables:

trap "rv=\$?; rm -rf $tmpdir; exit \$rv" EXIT

You might also want to escape $tmpdir, as it will get evaluated when the trap line gets executed and if the tmpdir value changes later that might not give the expected behaviour.

Edit: Use shellcheck to check your bash scripts and be aware of problems like this.

Paul Tobias
  • 1,962
  • 18
  • 18
58

I think you can use $? to get the exit code.

bmk
  • 13,849
  • 5
  • 37
  • 46
  • 3
    @Todd: The variables `$BASH_COMMAND` and `$BASH_LINENO` come in handy sometimes, too. – Dennis Williamson Mar 15 '11 at 15:18
  • 7
    @Todd, @bmk : don't forget that *any* command executed changes the value of $?; for example, myCmdIWantToTest ; echo $? ; myRC=$? ; ... now myRC is the 'truth' of the echo $? cmd being invoked. If myCmdIWantToTest exited 'false', that value is lost. It is best to save $? to a separately named var that is unique. Using exitCode=$? universally, you can easily wind-up inheriting some other cmd's exitCode (or more likely from a sub-shell(script) ). Also, don't forget that it is OK test exit code as part of an if ; then ; ... fi. Like if myCmdIWantToTest ; then echo worked; else echo failed; fi – shellter Mar 16 '11 at 03:15
9

I've found it is better to separate EXIT trap from the trap for other signals

Example trap test script...

umask 77
tmpfile=`tmpfile.$$`
trap 'rm -f "$tmpfile"' EXIT
trap 'exit 2' HUP INT QUIT TERM

touch $tmpfile
read -r input 

exit 10

The temporary file is cleaned up. The file exit value of 10 is preserved! Interrupts result in an exit value of 2

Basically as long as you don't use "exit" in a EXIT trap, it will exit with the original exit value preserved.

ASIDE: Note the quoting in the EXIT trap. That lets me change what file needs to be cleaned up during the scripts lifetime. I often also include a test for the existence of the $tmpfile before trying to remove it, so I don't even need to set it at the start of the script, only before creating it.

anthony
  • 7,696
  • 1
  • 17
  • 11
5

The following code works well. You can store the exit code and define the commands that are needed for each exit code in the trap function.

#!/bin/bash
trap cleanup EXIT
cleanup() {
   exit_code=$?
   if [[ ${exit_code} -eq 1 ]]; then
       # command 1
   elif [[ ${exit_code} -eq 2 ]]; then
       # command 2
   elif [[ ${exit_code} -eq 3 ]]; then
       # command 3
   fi
}
fflores
  • 192
  • 1
  • 6