| Tested on | RHEL 10.2 (Coughlan) |
|---|---|
| Package | bash 5.2.26-6.el10coreutils 9.5-8.el10_2 |
| Applies to | GNU/Linux distributions with Coreutils mktemp |
| Privilege | Normal user |
| Scope | GNU mktemp for temporary files and directories: templates, --suffix, TMPDIR, permissions, failure handling, trap EXIT cleanup, multi-file temp workspaces, why mktemp -u is unsafe, and atomic replacement on one filesystem. Does not cover full signal-trap reference or every TMPDIR policy on shared systems. |
| Related guides | Bash shell script tutorial Shell scripting course hub |
mktemp creates a uniquely named temporary file or directory in one operation and prints its path. In Bash scripts, pair it with an EXIT trap so temporary data is removed even when the script exits early.
Avoid predictable paths such as /tmp/myapp.$$. Safe temporary-file creation requires choosing the name and creating the object without a race window between those steps.
Why Predictable /tmp Names Are Unsafe
A predictable name such as /tmp/myapp.$$ looks convenient:
echo "/tmp/myapp.$$"/tmp/myapp.14122A predictable temporary path is unsafe because another process may create the path before your script opens it, potentially as a symlink or another object your script did not create. A separate "does this name exist?" check makes the race even more obvious, but avoiding that check alone does not make predictable temporary names safe.
mktemp selects and creates the object in one operation. GNU documents that as the secure alternative to generating a pathname separately and opening it later.
Timestamps, $RANDOM, and long UUID-like strings in the filename do not fix the race unless creation is atomic the way mktemp performs it.
Create a Temporary File with mktemp
With no arguments, GNU mktemp creates a file under $TMPDIR when that variable is set, otherwise under /tmp:
tmpfile=$(mktemp)
stat -c '%A %a %n' "$tmpfile"-rw------- 600 /tmp/tmp.jneXbvW49qGNU mktemp creates files with user read/write permission (u+rw), further restricted by the current umask. With a typical umask, that produces mode 600 (rw-------). Always quote "$tmpfile" when you use the path.
Register cleanup as soon as the path is valid. The Bash error handling guide shows the same trap EXIT pattern for temp directories:
tmpfile=$(mktemp) || exit 1
trap 'rm -f "$tmpfile"' EXITCreate a Temporary Directory with mktemp -d
Use -d when the script needs a workspace rather than a single file:
tmpdir=$(mktemp -d)
stat -c '%A %a %n' "$tmpdir"drwx------ 700 /tmp/tmp.lSjZW6pD2lGNU mktemp creates directories with user read/write/search permission (u+rwx), further restricted by umask. With a typical umask, that is mode 700. A temp directory is often cleaner when the workflow creates several intermediate files, downloads, or validation artifacts.
Prefer a temporary directory when a script creates several related intermediate files. One EXIT trap can then remove the entire workspace instead of tracking each temporary file separately.
Use Templates and TMPDIR
GNU Coreutils mktemp options you are most likely to use in scripts:
| Option | Purpose |
|---|---|
-d, --directory |
Create a directory |
-p DIR, --tmpdir=DIR |
Create relative to a specific temp directory |
--suffix=SUFFIX |
Add a suffix |
-q, --quiet |
Suppress creation-error messages |
-u, --dry-run |
Print an unused name without creating it; unsafe for temp creation |
A GNU mktemp template needs at least three consecutive X characters in its final path component. The final run of X characters is replaced with random alphanumeric characters; a suffix such as .txt may follow it. mktemp creates only the final component; parent directories must already exist.
mktemp /tmp/demo.XXXXXX/tmp/demo.y3Y8hPAdd a file extension with --suffix
With an explicit suffix, the template must end in X. An explicit --suffix value cannot contain /:
tmpfile=$(mktemp --suffix=.txt)
echo "$tmpfile"/tmp/tmp.K9DOKqgp5t.txtHere .txt is the explicit suffix GNU appends after replacing the final X run.
Without --suffix, GNU infers one by locating the last X in the template. A descriptive basename can combine --tmpdir with a template whose extension follows the X run:
mktemp --tmpdir=/tmp report.XXXXXX.txt/tmp/report.UNu0kL.txtHere GNU infers .txt as the suffix because the supplied template does not end in X.
Point TMPDIR at an existing directory when you want temporary files under a workspace you control. This standalone example removes the file and directory explicitly afterward:
tmpbase=$(mktemp -d)
tmpfile=$(TMPDIR="$tmpbase" mktemp)
echo "$tmpfile"
rm -f "$tmpfile"
rmdir "$tmpbase"/tmp/tmp.ZQkU1kyHXO/tmp.MrlZforqL9--tmpdir[=DIR] treats the template as relative to DIR. With bare --tmpdir, GNU mktemp uses $TMPDIR when set and otherwise /tmp—the same parent-directory logic as a no-argument call. A relative template without --tmpdir is created in the current directory instead. Pin DIR when you want a predictable location:
mktemp --tmpdir=/tmp bash-mktemp.XXXXXX/tmp/bash-mktemp.Vjjr3tQuote every returned path. Paths can contain characters that word-splitting or globbing would mishandle.
Check Creation Failure
mktemp prints the new path on success and exits non-zero when it cannot create the object. A bad template fails immediately:
mktemp /tmp/no-x-suffix 2>&1; echo "exit: $?"mktemp: too few X's in template '/tmp/no-x-suffix'
exit: 1Under set -e, a failed mktemp stops the script unless you handle the status. The Bash exit codes article covers testing $? when you need a custom message instead of errexit alone.
Typical pattern with Bash strict mode enabled:
set -euo pipefail
tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' EXITIf mktemp fails, the assignment returns non-zero, errexit exits, and the EXIT trap has nothing to remove yet.
Clean Up with trap EXIT
Duplicating rm on every error branch is brittle. Register one EXIT trap right after successful creation:
#!/usr/bin/env bash
set -euo pipefail
tmpdir=$(mktemp -d)
cleanup() { rm -rf "$tmpdir"; printf 'cleaned %s\n' "$tmpdir" >&2; }
trap cleanup EXIT
echo "working in $tmpdir"
echo data > "$tmpdir/file.txt"
falseWhen the final command fails, the trap still runs:
working in /tmp/tmp.Uee13cetMy
cleaned /tmp/tmp.Uee13cetMyAn EXIT trap runs when the shell exits normally or because of exit / set -e. SIGKILL cannot be trapped, so no shell cleanup can run after it. For signals such as INT and TERM, install handlers that exit with the appropriate status and let the EXIT trap perform cleanup:
trap cleanup EXIT
trap 'exit 130' INT
trap 'exit 143' TERMExiting from the INT handler runs cleanup once through the EXIT trap. Do not call cleanup from both INT and EXIT.
Why mktemp -u Is Unsafe
GNU mktemp -u prints a currently unused temporary name but does not create anything:
name=$(mktemp -u /tmp/unsafe.XXXXXX)
echo "name=$name"
test -e "$name" && echo "exists" || echo "does not exist"name=/tmp/unsafe.bihuGI
does not existYou are back to the two-step race: another process can create that path before your script does. The GNU manual documents -u as unsafe for secure temp creation. Do not use it when you need a real temporary object.
Temporary Files and Sensitive Data
mktemp protects temporary-file creation from pathname races; it does not encrypt the contents. Avoid putting secrets in filenames or command-line arguments, and remember that privileged users may still inspect temporary data.
Files your script creates inside a temporary directory use their own creation modes and the process umask:
tmpdir=$(mktemp -d)
echo secret > "$tmpdir/data"The directory itself is restrictive, but the inner file is created by the shell redirection according to the normal open mode and umask—not by mktemp.
Practical Example: Build Then Atomically Install a File
A common pattern writes and validates on the same filesystem as the final path, then renames into place so readers never see a half-written target file. Use a real destination directory—not a throwaway mktemp -d workspace—and create the temporary file inside it:
#!/usr/bin/env bash
set -euo pipefail
install_dir="$HOME/.local/share/myapp"
final="$install_dir/config.conf"
mkdir -p "$install_dir"
tmpfile=$(mktemp --tmpdir="$install_dir" .config.conf.XXXXXX)
trap 'rm -f "$tmpfile"' EXIT
printf 'key=value\n' > "$tmpfile"
grep -q '^key=' "$tmpfile"
chmod 644 "$tmpfile"
mv -f "$tmpfile" "$final"
trap - EXIT
printf 'installed to %s\n' "$final"installed to /home/testuser/.local/share/myapp/config.confSet the desired permissions on the temporary file before the rename. The installed file keeps the temporary file's metadata; mktemp does not copy the mode from an existing destination file. With a typical umask, mktemp alone leaves mode 600—fine for a private config, but adapt the chmod line when you replace a file that should be world-readable.
After a successful rename, $tmpfile no longer exists, so clear the EXIT trap with trap - EXIT.
Creating the temporary file in the destination directory keeps it on the same filesystem as final, allowing mv to use a rename instead of falling back to cross-filesystem copy/remove. On a filesystem with normal atomic rename semantics, readers see either the old file or the new file rather than a partially written destination.
Common mktemp Mistakes
| Mistake | Why it hurts |
|---|---|
/tmp/app.$$ or timestamp names |
Predictable path + unsafe open semantics |
mktemp -u then touch or mkdir |
Reintroduces the race mktemp avoids |
| Template path with missing parent dir | GNU mktemp does not create parent directories |
Temp file in /tmp, final file elsewhere |
mv may copy across filesystems instead of atomic rename |
Unquoted $tmpfile |
Word-splitting or globbing breaks paths |
| Cleanup only on success | Error paths leak files without trap EXIT |
cleanup in both INT and EXIT traps |
Cleanup runs twice on Ctrl+C |
Fewer than three X characters in the final X run |
GNU mktemp rejects the template |
Summary
mktemp creates a temporary file or directory in one step, which is the property that makes it safer than predictable /tmp names built from $$, timestamps, or $RANDOM. Use mktemp for a single file, mktemp -d for multi-file work, templates with at least three consecutive X characters in the final path component, --suffix when you need an extension, and TMPDIR or --tmpdir when you want a specific parent directory.
Quote the returned path, exit when creation fails, and register trap EXIT cleanup immediately after success. Avoid mktemp -u for real temp objects. For atomic replacement, create the temp file in the destination directory on the same filesystem, set permissions before rename, validate, then mv it into the final name.
References
- GNU Coreutils —
mktempinvocation - GNU Coreutils — Temporary files
- GNU Coreutils —
mvinvocation - Bash manual — Bourne Shell Builtins (
trap)
Frequently Asked Questions
Is mktemp secure?
It is secure against the classic temp-file race when you use it to create the object (not mktemp -u). It does not encrypt contents or hide data from privileged users on the system.
What permissions does it use?
GNU mktemp creates files with user read/write permissions and directories with user read/write/search permissions, further restricted by the current umask. With a typical umask, that produces 600 files and 700 directories.
What do the X characters mean?
The final run of X characters in the template is replaced with random alphanumeric characters. That run must contain at least three consecutive X characters, and a suffix such as .txt may follow it.
Why avoid mktemp -u?
-u prints a currently unused name but does not create anything, so another process can claim the path before your script does.
How do I clean up after Ctrl+C?
Register trap cleanup EXIT right after mktemp, then use trap 'exit 130' INT and trap 'exit 143' TERM. The signal handler sets the exit status; exiting runs cleanup once through the EXIT trap.

