mktemp Command: Creating Safe Temporary Files
What You'll Learn
- The safe pattern for creating temporary files and directories with
mktemp - Why
file.$$and fixed-name temp files are dangerous - How to pair
mktempwithtrapso cleanup always runs
Quick Summary
- Temp file →
tmp=$(mktemp) - Temp directory →
dir=$(mktemp -d) - Always add
trap 'rm -rf "$dir"' EXITto clean up automatically
Prerequisites
- GNU coreutils (check with
mktemp --version; verified here on 9.4) - A typical Linux distribution (Ubuntu, RHEL family, etc.)
- BSD / macOS
mktemphas a different option set
What Is mktemp?
Conclusion: mktemp safely creates a uniquely named temporary file or directory and prints its path to stdout.
mktemp creates a non-colliding temporary file and prints its path, so your program never has to invent file names itself.
$ mktemp /tmp/tmp.A1b2C3d4E5
Omit the template and it creates tmp.XXXXXXXXXX under $TMPDIR (or /tmp if unset). The X characters are replaced with random characters.
From the man page (GNU coreutils 9.4):
Create a temporary file or directory, safely, and print its name. Files are created u+rw, and directories u+rwx, minus umask restrictions.
The two key words are "safely" and "print its name": creation and naming happen atomically, and only the owner can read or write the result.
Why Use mktemp?
Conclusion: Fixed names and
$$-based (PID) names are predictable, inviting races, overwrites, and symlink attacks. mktemp prevents this by design.
Dangerous patterns to avoid
# BAD: fixed name tmpfile=/tmp/myapp.tmp # BAD: PID is predictable and collides under concurrency tmpfile=/tmp/myapp.$$
The problems:
- Race condition: running the same script concurrently makes the names collide and corrupt each other's data
- Symlink attack: an attacker who guesses the name can pre-place a symlink in
/tmp, so your script overwrites an unintended file - Leaky permissions: creating via
touchor>depends on umask and may be world-readable
What mktemp fixes
$ tmpfile=$(mktemp) $ stat -c '%a %n' "$tmpfile" 600 /tmp/tmp.A1b2C3d4E5
mktemp performs creation and naming as one indivisible step and fails if the file already exists. Files are created u+rw (typically 600 after umask) and directories u+rwx (typically 700), so they are owner-only from the start.
As long as the name is predictable, a later chmod cannot close the race window. What matters is that the file is safe at the moment of creation.
How Do You Create Temp Files and Directories?
Conclusion: Use
mktempfor a file andmktemp -dfor a directory; capture the returned path in a variable and use that variable from then on.
Temporary file
$ tmpfile=$(mktemp) $ echo "data" > "$tmpfile" $ cat "$tmpfile" data
Temporary directory
$ tmpdir=$(mktemp -d) $ echo "$tmpdir" /tmp/tmp.Xy9Zq2Lk7P
-d (--directory) creates a directory instead of a file. Use it when you need several intermediate files in one place, then clean up with a single rm -rf "$tmpdir".
Always double-quote the variable ("$tmpfile"). It keeps working even if TMPDIR points to a path containing spaces.
How Do You Control the Location and Name?
Conclusion: A template shapes the name,
-psets the base directory, and--suffixadds an extension. The template needs at least three consecutive trailingXs.
Pass a template
$ mktemp myapp.XXXXXX myapp.k3Df9a
The Xs are replaced with random characters. GNU requires at least three consecutive Xs in the last component.
Set the base directory (-p / --tmpdir)
$ mktemp -p /var/tmp myapp.XXXXXX /var/tmp/myapp.q7Zb2K
-p DIR (--tmpdir[=DIR]) sets the base directory. Because /tmp may be wiped on reboot, pick /var/tmp for temporary data that should survive a little longer.
Add an extension (--suffix)
$ mktemp --suffix=.log myapp.XXXXXX myapp.a8Kd2p.log
Useful when a tool detects the format by extension. SUFF must not contain a slash.
Template vs -p / -t (notes)
- With
-p DIR, the template must not be absolute. It may contain slashes, butmktemponly creates the final component. -t("treat the template as a single filename component under$TMPDIRetc.") is an older, now deprecated option. Use-pin new scripts.
How Do You Clean Up Reliably in a Script?
Conclusion: Add
trap 'rm -rf "$tmpdir"' EXITright after creation, and the temp data is removed on both normal and error exits.
The standard pattern for a script that creates temp files:
#!/usr/bin/env bash set -euo pipefail tmpdir=$(mktemp -d) trap 'rm -rf "$tmpdir"' EXIT # use $tmpdir freely here curl -s https://example.com/data.json > "$tmpdir/data.json" jq '.items' "$tmpdir/data.json" # on exit, the trap removes $tmpdir automatically
Key points:
- Set the
trapimmediately aftermktemp -d(write creation and cleanup together) - Trapping
EXITmeans cleanup runs even whenset -eaborts midway - A single directory lets you
rm -rfeverything regardless of how many files you add
See Handling Signals and Cleanup with trap for details.
Limit the trap target to $tmpdir (the unique path mktemp returned). A script that rm -rfs a broad pattern like /tmp/* is an accident waiting to happen.
Options and Pitfalls Worth Knowing
Conclusion:
-u(dry-run) only prints a name without creating anything, breaking the safety guarantee. Default to plainmktemp, which creates the file for you.
| Option | Meaning | Caution |
|---|---|---|
-d |
Create a directory, not a file | Clean up with rm -rf |
-u |
Print a name only, do not create (dry-run) | Opens the race window. Avoid it. |
-q |
Suppress creation-failure diagnostics | When the script handles errors itself |
-p DIR |
Set the base directory | Template must not be absolute |
--suffix=SUFF |
Append an extension, etc. | No slash allowed |
Taking a name from -u and touching it yourself defeats the purpose of mktemp. Let mktemp handle everything from creation to returning the name.
Watch the return value too:
# BAD: if creation fails, proceeding may rm an empty string tmpfile=$(mktemp) # GOOD: detect failure and stop tmpfile=$(mktemp) || exit 1