1

Pursuant to this issue which I'm not permitted to comment on:

Is it necessary to specify traps other than EXIT?

IMHO, this is an incomplete answer because it doesn't cover the usual case which is:

TEMPDIR_OR_FILE=$(mktemp [ some switches and params and an XXX pattern] )
# ...so we want trap to do rm -rf ${TEMPDIR_OR_FILE}

The examples given are:

# reset
trap 'excode=$?; cmd; trap - EXIT; echo $excode' EXIT HUP INT QUIT PIPE TERM

# ignore
trap 'excode=$?; trap "" EXIT; cmd; echo $excode' EXIT HUP INT QUIT PIPE TERM

The problem here is that the single quotes prevent you from expanding ${TEMPDIR_OR_FILE}

What I perceive as a complication for this is the time at which the variables are expanded. I need ${TEMPDIR_OR_FILE} expanded immediately but the other items probably need to wait until the trap is executed. How do I make this work?

  • As an aside -- all-caps names are used for variables with meaning to the OS or system, whereas names with at least one lower-case character are reserved for application use and guaranteed not to modify behavior of POSIX-specified tools. See http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html, fourth paragraph. – Charles Duffy Oct 13 '17 at 17:03
  • That said, **why** do you want to expand `TEMPDIR_OR_FILE` when the trap is defined, not when it's executed? Doing this later at execution time (by having the definition be inside outer single quotes) will let you change which temporary files are in use. – Charles Duffy Oct 13 '17 at 17:05
  • 1
    In my experience this hasn't been a problem. Such vars are usually set once, possibly even as read-only, and then used for the rest of the program. Deferred expansion doesn't change anything. Why do you need it parsed sooner? I could probably work out a kludge, but hesitate to add the maintenance headache. – Paul Hodges Oct 13 '17 at 17:16

1 Answers1

3

Best Practice: Expand At Execution Time

Simple Case

The code you were worried about,

trap 'EXIT' 'rm -f "$TEMPDIR_OR_FILE"'

...really does work; it just looks up TEMPDIR_OR_FILE when the trap is running, not when it's defined. There's nothing wrong with that: When the trap is running is the best time to look at your temporary file definitions, because that way the rest of your script can change those definitions as appropriate during its execution.

Getting Fancy: Handling An Arbitrary Number Of Temporary Files

Consider the following:

declare -A tempfiles=( )
cleanup() { (( ${#tempfiles[@]} )) && rm -rf -- "${!tempfiles[@]}"; }
trap 'cleanup' EXIT

# ...do some stuff...
tempfiles[something]=$(mktemp -t -d something.XXXXXX)
echo "hello" >"${tempfiles[something]}/greeting"

# ...do more stuff...
tempfiles[something_else]=$(mktemp -t something_else.XXXXXX)
if [[ $keep_something_else ]]; then
  # the user wants us to keep this temporary file! remove it from deletion list
  unset tempfiles[keep_something_else]
fi

With the above, you define your cleanup function just once, and don't need to know your temporary directories at that time; when you exit, you look up the list as it exists then.


Literal Answer: Expanding At Definition Time

In most cases, it's desirable to expand a variable when a trap is executed, vs when it's defined. However, you're asking to do it the other way.

To do this safely, use printf %q to generate an eval-safe version of your filename, and expand that in double quotes when defining the trap.

printf -v tempdir_or_file_q '%q' "$TEMPDIR_OR_FILE"
trap 'retval=$?; rm -rf '"${tempdir_or_file_q}"'; exit "$retval"'

Note that we end the initial single quotes before referencing the variable you want to expand at definition time, expand it in double quotes, end those double quotes and switch back to single quotes after.

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441