94

I'm looking at a bash script someone else wrote that uses mktemp:

TEMP=`mktemp --directory`

However, this line does not work on my machine (OS X 10.6).

How would I fix this line so that it is cross-un*x-like-platform compatible? EDIT: An alternative command would be sufficient as well.

1

5 Answers 5

139

The following is what I ended up using to reliably create a temporary directory that works on both Linux and Darwin (all versions before Mac OS X 10.11), without hardcoding $TMPDIR or /tmp:

mytmpdir=$(mktemp -d 2>/dev/null || mktemp -d -t 'mytmpdir')

Background:

The GNU mktemp command requires no arguments. Plain mktemp will work and creates a temporary file in the system's temporary directory.

Plain mktemp -d will create a directory instead of a file, which is what you'd want to use on Linux.

(gnu-coreutils)$ man mktemp
> ..
> If DIR is not specified, uses $TMPDIR if set, else /tmp.
> ..

By default, GNU mktemp uses the template tmp.XXXXXXXXXX for the name of the sub directory (or file). To customise this template, the -t option can be used.

OSX's mktemp has no default template and requires a template to be specified. Unfortunately, where GNU mktemp takes the template as -t option, on OSX this is passed as positional argument. Instead, OSX's mktemp has a -t option that means something else. The -t option on OSX is documented as a "prefix" for the template. It is expanded to {prefix}.XXXXXXXX, so it adds the Xs to it automatically (e.g. mktemp -d -t example could create example.zEJZWCTQ in the temp directory).

I was surprised to find that in many Linux environments, $TMPDIR is not set by default. Many CLI programs do support it when set, but still need a default for /tmp. This means passing $TMPDIR/example.XXXXXXXX to mktemp or mkdir is dangerous because it may produce /example.XXXXXXXX in the root directory of the local disk (due to $TMPDIR being unset and becoming an empty string).

On OSX, $TMPDIR is always set and (at least in the default shell) it is not set to /tmp (which is a symlink to /private/tmp) but to /var/folders/dx/*****_*************/T. So whatever we do for OSX, should honour that default behaviour.

In conclusion, the following is what I ended up using to reliably create a temporary directory that works on both Linux and Darwin (Mac OS X), without hardcoding either $TMPDIR or /tmp:

mytmpdir=$(mktemp -d 2>/dev/null || mktemp -d -t 'mytmpdir')

The first part is for Linux. This command will fail on Darwin (Mac OS X) with error status code 1 responding with "usage: ...". That's why we ignore stderr and instead then execute the Mac variant. The mytmpdir prefix is only used on Mac (where that option is required to be set).

11
  • 15
    Really cool. Sometimes OS X different behaviour is annoying. Aug 22, 2013 at 9:55
  • 9
    @therealmarv I'd rather say, always. Nov 26, 2013 at 18:44
  • 14
    I just ran mktemp -d on OS X 10.11.5 El Capitan, and it worked as expected: file $(mktemp -d): /var/folders/j4/htlnmbf97vlcdszj7_x8g0vh4k3_fp/T/tmp.JXmsrQnL: directory Jul 15, 2016 at 18:50
  • 2
    Yep. I was surprised as you were that this Just Worked™. Jul 22, 2016 at 18:02
  • 3
    The man page for my OSX (10.14.5 Mojave) says If no arguments are passed or if only the -d flag is passed mktemp behaves as if -t tmp was supplied. Nov 1, 2019 at 14:29
17

You have to supply a template. mktemp -d /tmp/foo.XXXX should work. I've never seen --directory. The -- suggests that it is a GNU extension.

2
  • 6
    If you want to use $TMPDIR when available, do mktemp -d "${TMPDIR:-/tmp}"/foo.XXXX. That works in bash on the Mac and dash on Debian, so it seems portable. Apr 15, 2015 at 7:36
  • 1
    @TomAnderson It also works with FreeBSD mktemp(1). Feb 11, 2020 at 10:07
9

Change --directory to -d. The former is a GNU-ism, but GNU mktemp from coreutils also supports -d. The mktemp in OS X is the same as from BSD, so -d should be pretty portable among systems that actually ship a mktemp program.

4
  • I just tried mktemp -d, and it does not work either. Jan 26, 2012 at 19:56
  • 1
    Kyle has the complete answer. The mktemp on OS X requires a template. The script you're using assumes GNU conventions, which uses a default template if none is supplied. Jan 26, 2012 at 21:24
  • The problem is that unix expects XXXXX to be supplied in the template where as OS X does not sure if there is a compatible template flag? Mar 15, 2016 at 7:06
  • 2
    mktemp on OS X 10.11.5 El Capitan works for me: file $(mktemp -d): /var/folders/j4/htlnmbf97vlcdszj7_x8g0vh4k3_fp/T/tmp.JXmsrQnL: directory Jul 15, 2016 at 18:52
6
temp_dir="$(mktemp -q -d -t "$(basename "$0").XXXXXX")"
  • mktemp for BSD (including OSX) requires a template, but it allows any number of Xs in the template.
  • (GNU) mktemp for Linux does not require a template, however, if a template is specified, then the number of Xs must be 6.

Note that -t is deprecated for GNU mktemp, so a more future-proof code would be

temp_dir="$(mktemp -q -d -t "$(basename "$0").XXXXXX" 2>/dev/null || mktemp -q -d)"
0

The best solution I found to be sure that I create the temporary folder on the /tmp directory on both Linux and Mac OS is mktemp -d '/tmp/mytmpdir.XXXXXX'

Hope is helpful.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .