mktemp Command: Creating Safe Temporary Files

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 mktemp with trap so cleanup always runs

Quick Summary

  • Temp file → tmp=$(mktemp)
  • Temp directory → dir=$(mktemp -d)
  • Always add trap 'rm -rf "$dir"' EXIT to 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 mktemp has 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 touch or > 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 mktemp for a file and mktemp -d for 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, -p sets the base directory, and --suffix adds an extension. The template needs at least three consecutive trailing Xs.

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, but mktemp only creates the final component.
  • -t ("treat the template as a single filename component under $TMPDIR etc.") is an older, now deprecated option. Use -p in new scripts.

How Do You Clean Up Reliably in a Script?

Conclusion: Add trap 'rm -rf "$tmpdir"' EXIT right 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 trap immediately after mktemp -d (write creation and cleanup together)
  • Trapping EXIT means cleanup runs even when set -e aborts midway
  • A single directory lets you rm -rf everything 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 plain mktemp, 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

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

Summary / Next Reading