Intermediate · 10 min · practise every command in the built-in terminal

Safer Scripts: mktemp and trap

A script that works on a temporary file has two jobs besides its real one: pick a name nobody else uses, and clean up — also when something goes wrong halfway.

mktemp — a temporary file of your own

mktemp                       # /tmp/tmp.k3F9x2LmQa
mktemp -d                    # a temporary folder
mktemp /tmp/report.XXXXXX    # your own name: the X's become random letters

mktemp creates the file and prints its name. Why not simply /tmp/report.txt?

  • two runs of the script at the same time would write into the same file
  • another user could have made that name first (even as a link to one of your files)
  • mktemp's file is new, its name unguessable, and only you may read it:
TMP=$(mktemp)
ls -l "$TMP"                 # -rw------- 1 student student 0 ... /tmp/tmp.Xy7...

trap … EXIT — clean up whatever happens

trap 'rm -f "$TMP"' EXIT

trap 'COMMANDS' EXIT runs COMMANDS when the script ends: at the last line, at an exit, or when set -e stops it after an error. Put it right after the mktemp:

#!/bin/bash
TMP=$(mktemp)
trap 'rm -f "$TMP"; echo "cleaned up $TMP"' EXIT

ls /etc > "$TMP"
echo "files in /etc: $(wc -l < "$TMP")"
files in /etc: 46
cleaned up /tmp/tmp.EnSZXU8Wzt

The single quotes matter: $TMP is read when the trap runs, not when it's set.

trap … ERR — when a command fails

#!/bin/bash
set -euo pipefail
trap 'echo "Something failed — stopping." >&2' ERR

cp /etc/hostname /tmp/
cp /no/such/file /tmp/       # fails: the ERR trap speaks, set -e stops the script
echo "never printed"

Signals: Ctrl+C and kill

A trap can also catch a signal — INT is Ctrl+C, TERM is what kill sends by default:

trap 'echo "Interrupted — cleaning up"; exit 130' INT TERM

Which traps are set

trap -p                      # trap -- 'echo ...' SIGINT
trap - INT                   # remove a trap
trap -l                      # every signal's name and number
TrapRuns when
EXITthe script ends, however it ends
ERRa command fails
INTCtrl+C
TERMkill PID

💡 Every script that makes a temporary file should have the pair TMP=$(mktemp) + trap 'rm -f "$TMP"' EXIT — nothing is left behind in /tmp.

Practice tasks

  1. Make a temporary file with mktemp
  2. Make a temporary folder
  3. Write ~/scripts/report.sh: it makes a temporary file with mktemp and removes it in a trap … EXIT
  4. Run your script and see the cleanup happen
  5. Set a trap for Ctrl+C in your shell, then list the traps

Commands in this lesson

Shell Scripting