macOS `mktemp: unrecognized option --suffix`: a template like `report.XXXXXX.txt` creates that exact name, then "File exists"
A script that needs a temporary file with an extension stops on a Mac:
$ mkdir demo && cd demo
$ mktemp --suffix=.txt; echo "exit $?"
mktemp: unrecognized option `--suffix=.txt'
usage: mktemp [-d] [-p tmpdir] [-q] [-t prefix] [-u] template ...
mktemp [-d] [-p tmpdir] [-q] [-u] -t prefix
exit 1
--suffix is a GNU mktemp option (from GNU’s documentation; I didn’t run
GNU mktemp here). macOS ships BSD mktemp. There’s no --version; what
shows the build:
$ mktemp --version; echo "exit $?"
mktemp: unrecognized option `--version'
usage: mktemp [-d] [-p tmpdir] [-q] [-t prefix] [-u] template ...
mktemp [-d] [-p tmpdir] [-q] [-u] -t prefix
exit 1
$ what /usr/bin/mktemp
/usr/bin/mktemp:
PROGRAM:mktemp PROJECT:shell_cmds-329
PROGRAM:mktemp PROJECT:shell_cmds-329
The X’s in the middle: no error, no random part
The next thing to try is putting the extension after the X’s:
$ mktemp ./report.XXXXXX.txt; echo "exit $?"
./report.XXXXXX.txt
exit 0
$ ls
report.XXXXXX.txt
Nothing was replaced. The file is named report.XXXXXX.txt, and mktemp
exited 0, so a script carries on. The second run of the same script fails:
$ mktemp ./report.XXXXXX.txt; echo "exit $?"
mktemp: mkstemp failed on ./report.XXXXXX.txt: File exists
exit 1
The man page says only trailing X’s are replaced:
$ man mktemp | col -b | grep -A1 'The trailing'
/tmp/temp.XXXX. The trailing ‘Xs’ are replaced with the current process
number and/or a unique letter combination. The number of unique file
A template with no X’s at all behaved the same way: it created that name and exited 0.
$ mktemp ./plain.txt; echo "exit $?"
./plain.txt
exit 0
-t doesn’t help either. It treats its argument as a prefix and adds its
own random part after it, so the .txt ends up in the middle (-p . puts
the file in the current folder; the random part comes out different each time, per the man page):
$ mktemp -p . -t report.txt
./report.txt.kEVifr9SFB
Fix: a temp directory, a fixed name inside
mktemp -d makes a new, empty directory with a random name (the six
characters differ on every run). Inside it, the file name can be anything:
$ d=$(mktemp -d ./work.XXXXXX) && f="$d/report.txt" && : > "$f" && echo "$f"
./work.GLJA4i/report.txt
$ ls "$d"
report.txt
Cleanup is one rm -r "${d:?}" at the end:
$ rm -r "${d:?}" && [ ! -e "$d" ] && echo removed
removed
I didn’t test cleanup with trap here.
Rename after mktemp: mv -n can skip silently
Another common answer is to create report.XXXXXX and rename it to
report.XXXXXX.txt (random part comes out different each time, per the man page):
$ f=$(mktemp ./report.XXXXXX) && mv -n "$f" "$f.txt" && echo "$f.txt"
./report.8d6hdc.txt
-n keeps mv from overwriting a file that already has the .txt name.
But when the target existed, mv -n on this Mac didn’t move anything and
still exited 0:
$ printf 'new\n' > src; : > taken.txt
$ mv -n src taken.txt; echo "exit $?"
exit 0
$ ls src taken.txt
src
taken.txt
$ wc -c < taken.txt
0
So a script that checks only mv’s exit status would go on writing to a
.txt path it didn’t create. The mktemp -d version above doesn’t have
this step.
From Python
If the script is Python anyway, tempfile.mkstemp takes a suffix (the
random part comes out different each time, per the man page):
$ /usr/bin/python3 -I -c 'import tempfile,os; fd,p=tempfile.mkstemp(suffix=".txt",dir="."); os.close(fd); print(os.path.basename(p))'
tmp3ngg73ki.txt
Checklist
mktemp: unrecognized option --suffix:/usr/bin/mktempon macOS is BSD mktemp.- Only trailing X’s are replaced.
mktemp ./report.XXXXXX.txtcreated a file with exactly that name and exited 0; the second run failed with “File exists”. -t nameadds the random part after the name, so the extension doesn’t end the file name.- Temp file with an extension:
d=$(mktemp -d ./work.XXXXXX), then use"$d/report.txt". mv -nonto an existing file did nothing and exited 0 here. Don’t treat its exit status as “renamed”.
Tested on macOS 26.5.2 (Apple silicon) with /usr/bin/mktemp
(PROGRAM:mktemp PROJECT:shell_cmds-329), /bin/mv (file_cmds-479), and /usr/bin/python3
(Python 3.8.2) in zsh 5.9 (arm64-apple-darwin25.0). GNU coreutils and Linux
were not tested.