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
| Trap | Runs when |
|---|---|
EXIT | the script ends, however it ends |
ERR | a command fails |
INT | Ctrl+C |
TERM | kill 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.