Trap

From NovaOrdis Knowledge Base
Jump to navigation Jump to search

External

Internal

Overview

"trap" statement is a bash facility that allows for code execution upon catching specific signals. A common usage in shell scripts is to prevent a script to exit untimely when the user types a keyboard abort sequences, but run cleanup code instead. Conceptually, the trap facility is similar to the Java "finally" construct. When bash receives a signal for which a trap has been set while waiting for a command to complete, the trap will not be executed until the command completes. When bash is waiting for an asynchronous command via the wait built-in, the reception of a signal for which a trap has been set will cause the "wait" built-in to return immediately with an exit status greater than 128, immediately after which the trap is executed.

Syntax

trap <commands> <signals>

One or more signals can be listed. The signals can be listed with or without "SIG" prefix (SIGKILL and KILL are equivalent) or using their numeric correspondents. Aside from the standard Linux signals, bash comes with a special set of signals of its own.

Example:

trap 'rm -f ./lock' EXIT

Special Bash Signals

EXIT

"EXIT" is not a Linux signal, but a bash psuedo-signal, which is executed when the script exits. It can be used to make sure that the script executes some cleanup on exit.

DEBUG

For DEBUG, the list of commands is executed after every simple command in the script.

ERR

The trap commands are executed every time a script command exits with a non-zero exit status, unless the non-zero exits status comes from part of an "if" statement or from a "while" or "until" loop, or if a logical AND (&&) or OR (||) expression results in a non-zero exit code.

RETURN

Example:


Global variables declared before the trap declaration are correctly resolved when present in a single-quote quoted string (even if single-quotes are used, the single quote semantics when used in bash command line is different from that in effect here). For example, the following code:

a=hello
trap 'echo ${a}' EXIT

produces:

hello

Variable resolution is done at the time of execution, not declaration, so the following:

trap 'echo ${a}' EXIT
a=hello

also produces:

hello

Experiment with local variables. This works, explain this:

local chart_dir=blah
trap 'rm -rf '${chart_dir}'/tmpcharts; echo chart dir: ${chart_dir}' EXIT

produces:

chart dir: blah

This seems to work for local variables:

 trap 'rm -rf '${tmp_dir}' && debug '${tmp_dir}' removed; rm -rf '${chart_dir}'/tmpcharts && debug '${chart_dir}'/tmpcharts removed' EXIT

Only One Code Sequence (Latests) Executes

If multiple code sequences are declared with trap, only the last one is executed. The following example:

trap 'echo A' EXIT
trap 'echo B' EXIT

produces:

B

Behavior on Being Invoked from Sub-Shells

If code is registered with trap to react to EXIT in a sub-shell, or in a function that is invoked in a sub-shell, then the registered code will be executed when the sub-shell, and not the top-level invoking shell, exists.

The following code:

$(trap 'echo "a" 1>&2' EXIT)
echo "b"

will display:

a
b

Note that the output should be sent to stderr in the trap code - if the output is sent to stdout, the output is lost, even if the code executes.

Example

Delete Temporary Directory on Exit

local tmp_dir
local preserve_tmp_dir=false
tmp_dir=$(get-tmp-dir) || exit 1
if ${preserve_tmp_dir}; then
    debug "temporary directory ${tmp_dir} will be preserved on exit"
else
    trap "rm -rf ${tmp_dir} && debug deleted temporary directory ${tmp_dir} || warn failed to delete temporary directory ${tmp_dir}" EXIT && \
    debug "registered temporary directory ${tmp_dir} cleanup procedure"
fi

TODO

Reactive Wait Container

Investigate usefulness in case of a reactive wait container. Also see Docker Concepts - Container Exit.

CMD exec /bin/bash -c "trap : TERM INT; sleep infinity & wait"

Other Usage Patterns

trap "rm -f /tmp/blah" 0 2 3 5 10 13 15

Wrap the whole trap functionality in a function

trap _trap SIGTERM

function _trap() {
 ...
}