It’s easy to detect when a shell script starts, but it’s not always easy to know when it stops. A script might end normally, just as its author intends it to end, but it could also fail due to an unexpected fatal error. Sometimes it’s beneficial to preserve the remnants of whatever was in progress when a script failed, and other times it’s inconvenient. Either way, detecting the end of a script and reacting to it in some pre-calculated manner is why the Bash **trap** directive exists.

Responding to failure

Here’s an example of how one failure in a script can lead to future failures. Say you have written a program that creates a temporary directory in **/tmp** so that it can unarchive and process files before bundling them back together in a different format:

#!/usr/bin/env bash

## create tmp dir
mkdir "${TMP}"

## extract files to tmp
tar xf "${1}" --directory "${TMP}"

## move to tmpdir and run commands
pushd "${TMP}"
for IMG in *.jpg; do
 mogrify -verbose -flip -flop "${IMG}"
tar --create --file "${1%.*}".tar *.jpg

## move back to origin

## bundle with bzip2
bzip2 --compress "${TMP}"/"${1%.*}".tar \
    --stdout > "${1%.*}".tbz

## clean up
/usr/bin/rm -r /tmp/tmpdir

Most of the time, the script works as expected. However, if you accidentally run it on an archive filled with PNG files instead of the expected JPEG files, it fails halfway through. One failure leads to another, and eventually, the script exits without reaching its final directive to remove the temporary directory. As long as you manually remove the directory, you can recover quickly, but if you aren’t around to do that, then the next time the script runs, it has to deal with an existing temporary directory full of unpredictable leftover files.

One way to combat this is to reverse and double-up on the logic by adding a precautionary removal to the start of the script. While valid, that relies on brute force instead of structure. A more elegant solution is **trap**.

#bash #bash traps #tmp

Using Bash traps in your scripts
1.40 GEEK