mktemp — Safe Temporary Files and Directories
Create unique temporary files and directories safely. Avoids race conditions and predictable names by generating a random suffix. Essential for shell scripts that need scratch space. Syntax differs between GNU coreutils (Linux) and BSD (macOS).
mktemp creates temporary files and directories with a guaranteed-unique name, solving a real security problem: predictable names like /tmp/$$.tmp invite symlink attacks and race conditions, because an attacker can guess the name and plant a trap in /tmp ahead of time. Instead, mktemp generates a random suffix, creates the file atomically with mode 600 (directories with 700), and hands you back the path. Use -d to get a directory instead of a file. This guide covers the basics, templates, the differences between GNU (Linux) and BSD (macOS), and the indispensable cleanup with trap.
Basic Usage
mktemp — Create a unique temporary file in $TMPDIR (or /tmp). Returns the full path on stdout.
mktemp
# /tmp/tmp.A3kLp9mktemp -d — Create a unique temporary directory instead of a file.
mktemp -d
# /tmp/tmp.xY7qZ2<var>=$(mktemp) — Capture the path into a shell variable for later use.
tmp=$(mktemp)
echo "log data" > "$tmp"<var>=$(mktemp -d) — Capture a fresh temp directory path into a variable.
workdir=$(mktemp -d)
cd "$workdir"mktemp --help — Show all available options with short descriptions.
mktemp --helpTemplates (XXX Placeholders)
mktemp <template> — Use a custom name template. The trailing X characters (at least 3) are replaced with random characters.
mktemp mydata.XXXXXX
# mydata.k7Bq2pmktemp <prefix>.XXXXXXXXXX — More X characters means more entropy and lower collision probability. 10+ recommended for long-lived files.
mktemp build.XXXXXXXXXX
# build.Hf3k9pXw2Lmktemp -d <template> — Create a directory using a custom template.
mktemp -d myapp-XXXXXX
# myapp-9bTq2Kmktemp --suffix=<ext> <template> — Append a suffix after the random part. Useful for files that need a specific extension. GNU coreutils only.
mktemp --suffix=.json data.XXXXXX
# data.k7Bq2p.jsonmktemp <prefix>XXXXXX<suffix> — BSD/macOS alternative: place XXX in the middle of the name for a literal suffix.
mktemp build-XXXXXX.tar.gz
# build-k7Bq2p.tar.gzDirectory Options
mktemp -p <dir> — Create the temp file/directory inside
mktemp -p /var/tmp
# /var/tmp/tmp.A3kLp9mktemp --tmpdir=<dir> <template> — Long form of -p. Combine with a template.
mktemp --tmpdir=/var/cache app.XXXXXXmktemp -t <template> — Place the file in $TMPDIR (or a system default). Behaviour differs: on Linux -t is mostly a legacy alias; on BSD/macOS it is commonly used to set the prefix.
mktemp -t myappTMPDIR=<dir> mktemp — Override the default temp directory via environment variable for a single command.
TMPDIR=/fast/ssd mktemp -dmktemp -p "" <template> — Force relative placement: create in the current working directory instead of $TMPDIR.
mktemp -p "" cache-XXXXXXOther Flags
mktemp -u — Dry run: print a unique name without creating the file. UNSAFE — the name may be taken by another process before you use it. Avoid for real work.
mktemp -u
# /tmp/tmp.A3kLp9 (not created)mktemp --dry-run — Long form of -u. Same warning applies: only safe for preview purposes.
mktemp --dry-run --suffix=.log test.XXXXXXmktemp -q — Quiet mode. Suppress error messages if creation fails. Useful in scripts with custom error handling.
tmp=$(mktemp -q) || exit 1mktemp --version — Show the installed mktemp version. On Linux this tells you the coreutils release; on macOS it reports the BSD build.
mktemp --versionGNU (Linux) vs BSD (macOS)
mktemp (portable) — Plain form with no template works on both GNU and BSD. Safest default if you do not need a custom prefix.
tmp=$(mktemp) || exit 1mktemp -t <prefix> — GNU: prefix is optional; path goes to $TMPDIR. BSD/macOS: -t REQUIRES a prefix and inserts it before the random suffix.
# macOS:
mktemp -t myapp
# /var/folders/.../myapp.XXXXXXmktemp <prefix>.XXXXXX — Template form works on both. Must contain at least 3 X's at the END of the filename component on BSD (suffix via explicit characters after XXXXXX).
mktemp app.XXXXXX # both
mktemp app-XXXXXX.log # BSD ok, GNU needs --suffixmktemp --suffix=.ext — GNU only. BSD/macOS does NOT support --suffix. For cross-platform scripts use an inline suffix: prefix-XXXXXX.ext.
# Linux only:
mktemp --suffix=.sql dump.XXXXXXmktemp -p <dir> — GNU only. BSD does not support -p. For cross-platform scripts pass the directory inside the template or set TMPDIR.
# Portable:
TMPDIR=/var/tmp mktempCleanup Patterns (trap)
trap 'rm -f "$tmp"' EXIT — Remove a temp file when the script exits — normally, on error, or on Ctrl+C. Set the trap right after creating the file.
tmp=$(mktemp) || exit 1
trap 'rm -f "$tmp"' EXITtrap 'rm -rf "$dir"' EXIT — Recursive cleanup for a temp directory. Use -rf because the dir will usually contain files by the time the trap fires.
dir=$(mktemp -d) || exit 1
trap 'rm -rf "$dir"' EXITcleanup() { rm -rf "$dir"; }; trap cleanup EXIT — Use a function for more complex cleanup (close fds, kill background jobs, unmount loop devices, then remove the dir).
cleanup() {
[[ -n $pid ]] && kill $pid 2>/dev/null
rm -rf "$dir"
}
trap cleanup EXITtrap '...' EXIT INT TERM HUP — Also clean up on common signals. EXIT alone is enough in bash (EXIT fires after signals too), but explicit signals help in POSIX sh.
trap 'rm -rf "$dir"' EXIT INT TERM HUPtmp=$(mktemp) || { echo "mktemp failed" >&2; exit 1; } — Always check that mktemp succeeded before trusting the variable. Failure is rare but possible (full disk, bad permissions).
tmp=$(mktemp) || { echo "mktemp failed" >&2; exit 1; }
trap 'rm -f "$tmp"' EXITCommon Use Cases
Scratch file for command output — Capture output for later processing without risking a name collision in /tmp.
tmp=$(mktemp)
trap 'rm -f "$tmp"' EXIT
curl -s https://example.com > "$tmp"
grep -c "<h1>" "$tmp"Atomic file replacement — Write to a temp file in the same directory, then rename on top of the target. Rename is atomic on the same filesystem.
tmp=$(mktemp -p "$(dirname "$target")")
./generate > "$tmp" && mv "$tmp" "$target"Sorted/deduplicated edit in place — sort -o / sed -i exist, but mktemp gives you the pattern for tools that lack in-place support.
tmp=$(mktemp)
sort -u input.txt > "$tmp" && mv "$tmp" input.txtTemp workspace for extraction — Unpack an archive in an isolated directory, inspect it, then clean up automatically.
dir=$(mktemp -d)
trap 'rm -rf "$dir"' EXIT
tar -xzf archive.tar.gz -C "$dir"
find "$dir" -name '*.conf'Named pipe alternative via temp file — Pass two command outputs to a tool that expects two files (diff, cmp, comm).
a=$(mktemp); b=$(mktemp)
trap 'rm -f "$a" "$b"' EXIT
curl -s url1 > "$a"
curl -s url2 > "$b"
diff "$a" "$b"Temporary named pipe (FIFO) — mktemp creates a regular file; for a FIFO, create a temp DIR with mktemp -d, then mkfifo inside it.
dir=$(mktemp -d)
trap 'rm -rf "$dir"' EXIT
mkfifo "$dir/pipe"
producer > "$dir/pipe" &
consumer < "$dir/pipe"Security & Gotchas
Default mode is 0600 (600) — mktemp creates files with mode 600 (rw for owner only) and directories with 700. This is the main reason to use mktemp over ad-hoc /tmp/$$ patterns.
stat -c %a "$(mktemp)"
# 600Never use /tmp/$$.<name> — PID-based names are predictable. An attacker can create symlinks in /tmp that your script will follow and overwrite. Use mktemp to get an unguessable name.
# BAD: /tmp/mydata.$$
# GOOD: $(mktemp mydata.XXXXXX)Avoid -u / --dry-run for real files — -u only prints a name; a TOCTOU race window exists between 'print' and 'create'. If you must use -u, understand the risk.
# Race condition:
name=$(mktemp -u)
# ... attacker creates $name as a symlink ...
echo data > "$name" # follows symlinkAlways quote the variable — Paths may contain spaces (especially on macOS where /var/folders/.../ is used). Unquoted expansion will break.
# GOOD:
rm -f "$tmp"
# BAD:
rm -f $tmp # breaks on spaceClean up on early exit — Without a trap, aborted scripts leak files that may never be deleted (/tmp is cleared on reboot, /var/tmp is NOT).
# Without trap, Ctrl+C leaves $tmp behind:
tmp=$(mktemp) || exit 1
trap 'rm -f "$tmp"' EXITMind filesystem boundaries for atomic mv — mv is only atomic when source and target are on the SAME filesystem. Place the temp file in the same directory as the target when doing atomic replacement.
# Same filesystem as target:
tmp=$(mktemp -p "$(dirname "$target")") Conclusion
mktemp is a security tool, not just a convenience: it replaces the error-prone, predictable /tmp/$$ patterns with unguessable names, creates files atomically with mode 600, and thereby closes off symlink and TOCTOU attacks in shared directories. Remember three rules: always check the return value (|| exit 1), set a trap for cleanup immediately afterwards, and quote the variable on every use. Use -d for a directory; for cross-platform scripts, mind the differences between GNU and BSD (no -p/--suffix on macOS) and avoid -u/--dry-run for real files.
Further Reading
- GNU coreutils: mktemp invocation – official reference for every option
- Linux man page: mktemp(1) – the canonical manual page