Trap
External
- https://www.linuxjournal.com/content/bash-trap-command
- http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_12_02.html
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.
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 echo "bye" 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.
RETURN
ERR
Example:
trap 'rm -f ./lock' EXIT
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() {
...
}