PSA: mktemp Isn’t POSIX en 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 %!html: (``#!/bin/sh``) %!gmi: (#!/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 == %!html: - When writing a POSIX shellscript, make sure the commands you’re using are actually available in POSIX shell. You can do this easily by looking in the ``1p`` and ``8p`` sections of the manual. %!gmi: - When writing a POSIX shellscript, make sure the commands you’re using are actually available in POSIX shell. You can do this easily by looking in the 1p and 8p sections of the manual. % - Even if you know a command is specified by POSIX, still consider looking at the manual; Often commands will have slightly different syntax or fewer options than you’re used to. % %!html: - Use [shellcheck https://www.shellcheck.net/]. %!gmi: - Use shellcheck. %!gmi: [Shellcheck https://www.shellcheck.net/] == Sources == %!html: + Jan Schaumann, “Safely Creating And Using Temporary Files” In “Signs of Triviality”. 2017-06-05. https://www.netmeister.org/blog/mktemp.html %!gmi: + Jan Schaumann, “Safely Creating And Using Temporary Files” In “Signs of Triviality”. 2017-06-05. ''https://www.netmeister.org/blog/mktemp.html'' %!html: + Bruce Korb, “mktemp - Make a Temporary File or Directory”. 2002, updated 2014-08-30. https://www.gnu.org/software/autogen/mktemp.html %!gmi: + Bruce Korb, “mktemp - Make a Temporary File or Directory”. 2002, updated 2014-08-30. ''https://www.gnu.org/software/autogen/mktemp.html'' %!html: + Todd Miller, “Mktemp Manual”. 2014, updated 2015-11-22. https://www.mktemp.org/manual.html %!gmi: + Todd Miller, “Mktemp Manual”. 2014, updated 2015-11-22. ''https://www.mktemp.org/manual.html''