PSA: mktemp Isn’t POSIX

I’ve noticed quite a few shell scripts recently that claim to be POSIX-compliant despite using mktemp, so I wanted to go over a compliant alternative and a few things you should know about writing POSIX shell (#!/bin/sh) scripts.

Problems

Temporary files aren’t easy. They seem easy, but there are a lot of small details that cause security vulnerabilities if done incorrectly.[1] The best solution to this problem is to use mktemp(1), but that’s not guaranteed to be on all systems. Even if you’re comfortable assuming that mktemp will be present, its syntax differs on different systems.[2][3]

Solutions

As mentioned in [1], you could just avoid using temporary files — a multiline variable is often sufficient. If you absolutely need a temporary file, consider using the following function instead of relying on mktemp:

# POSIX sh lacks mktemp
mktemp() {
	# usage: file="$(mktemp [-d])"
	mktempOldUmask="$(umask)"
	umask u=rw,g=,o=

	if [ "$1" = "-d" ]; then
		# don't use m4 because it lacks mkdtemp()

		# use awk for randomness because POSIX sh lacks
		# $RANDOM, /dev/[u]rand, shuf, …
		printf '%s%s\n' "${TMPDIR:-/tmp}/tmp.$$." \
		"$(awk 'BEGIN{srand();printf("%d\n", rand()*10^7);}')"\
		| xargs -I _ -- sh -c \
			"mkdir -m u=rwx,g=,o= _ && echo _"
	else
		# use m4 to use the real mkstemp() function
		printf '%s\n' mkstemp(${TMPDIR:-/tmp}/tmp.XXXXXXX)"|m4
	fi

	umask "$mktempOldUmask"
}

This uses m4’s mkstemp() function (which is specified POSIX) for the creation of regular temporary files, and a racefree shell-solution for temporary directories.

Things to know

Sources

  1. Jan Schaumann, “Safely Creating And Using Temporary Files” In “Signs of Triviality”. 2017-06-05. https://www.netmeister.org/blog/mktemp.html
  2. Bruce Korb, “mktemp – Make a Temporary File or Directory”. 2002, updated 2014-08-30. https://www.gnu.org/software/autogen/mktemp.html
  3. Todd Miller, “Mktemp Manual”. 2014, updated 2015-11-22. https://www.mktemp.org/manual.html