72

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.

108

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).

  • 12
    Really cool. Sometimes OS X different behaviour is annoying. – therealmarv Aug 22 '13 at 9:55
  • 6
    @therealmarv I'd rather say, always. – Nikita Volkov Nov 26 '13 at 18:44
  • 10
    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 – Heath Borders Jul 15 '16 at 18:50
  • 2
    Yep. I was surprised as you were that this Just Worked™. – Heath Borders Jul 22 '16 at 18:02
  • 1
    Also if you run mktemp -d -t 'mytmpdir' on Linux (tested with mktemp from coreutils 8.26), it works as expected. – user100278 Feb 20 '17 at 9:06
15

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.

  • 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. – Tom Anderson Apr 15 '15 at 7:36
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.

  • I just tried mktemp -d, and it does not work either. – mindeavor. Jan 26 '12 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. – James Sneeringer Jan 26 '12 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? – user3467349 Mar 15 '16 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 – Heath Borders Jul 15 '16 at 18:52
4
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)"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.