# An atexit for Bash

I wrote the following atexit implementation for Bash

#! /bin/bash

set -e

ATEXIT=()

function atexit_handler
{
local EXPR
for EXPR in "${ATEXIT[@]}"; do echo "evaluating$EXPR"
eval "$EXPR" || true done } trap atexit_handler EXIT function atexit () { local EXPR for EXPR in "$@"; do
ATEXIT+=("$EXPR") done } atexit true atexit false atexit "echo bye" false  I am wondering, if this is possible without the use of eval. ## 2 Answers I don't think it's possible without the eval. A close candidate might be: atexit_handler() { local EXPR for EXPR in "${ATEXIT[@]}"; do
echo "evaluating $EXPR"$EXPR || true
done
}


But this won't work with non-trivial expressions like this:

atexit 'for i in "a b" c; do echo $i; done'  Using the function keyword in function declaration like this is an outdated practice: function atexit_handler { local EXPR for EXPR in "${ATEXIT[@]}"; do
echo "evaluating $EXPR" eval "$EXPR" || true
done
}


Use the more modern style I wrote in the previous example above.

Have you considered trapping other signals too than EXIT?

trap atexit_handler EXIT


A common practice in clean-up scripts is to handle 1 2 3 15, but it's up to you how you want to use this.

  for EXPR in "$@"; do ATEXIT+=("$EXPR")
done


A simpler way to iterate over $@:  for EXPR; do ATEXIT+=("$EXPR")
done


But actually, as @etan-reisner pointed out in a comment, it's silly to loop here when you can add the entire arg list in one swift move:

ATEXIT+=("$@")  • Why bother iterating over "$@" at all? Why not just splat it into the () directly? ATEXIT+=("$@") – Etan Reisner Oct 23 '14 at 18:45 • Using set -e means it works even when the script is run "manually" (i.e. bash myscript.sh) which the shebang does not. – Etan Reisner Oct 23 '14 at 18:46 • @EtanReisner spot on! Thanks for that. I added the first point to my answer and removed the second. – janos Oct 23 '14 at 20:34 • Emacs does not color the function names in sh-mode. Using the function keyword adds some syntax highlighting to functions. – ceving Oct 27 '14 at 9:51 Still uses eval, but it seems cleaner to do: atexit() { cmd="$1"
eval set -- $(trap -p EXIT) trap "$cmd; $3" EXIT }  To allow semi-colons (eg, atexit "echo foo;") and aesthetic purity, you might like: atexit() { cmd="${1%;}"
eval set -- $(trap -p EXIT) trap "$cmd${3:+; }$3" EXIT
}