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.

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.


Instead of 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+=("$@")
  • 2
    Why bother iterating over "$@" at all? Why not just splat it into the () directly? ATEXIT+=("$@") – Etan Reisner Oct 23 '14 at 18:45
  • 1
    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
}

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.