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.

  • See also: https://stackoverflow.com/questions/4632028/how-to-create-a-temporary-directory – Lenik Jul 22 '20 at 12:07

5 Answers5

140

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

dimo414
  • 1,797
Timo Tijhof
  • 7,170
  • 15
    Really cool. Sometimes OS X different behaviour is annoying. – therealmarv Aug 22 '13 at 09:55
  • 9
    @therealmarv I'd rather say, always. – Nikita Volkov Nov 26 '13 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 – Heath Borders Jul 15 '16 at 18:50
  • @HeathBorders: that seems to be the first version where that has changed ; on OS X 10.10.5, mktemp -d produces a usage message – ssc Jul 22 '16 at 15:08
  • 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. –  Feb 20 '17 at 09:06
  • @HeathBorders Does Just Worked™ mean there were no unwanted side-effects, or it Just Worked™ meaning it barely passed the threshold of functionality but the wheels are gonna fall off any second now – Davos Jun 06 '18 at 09:19
  • I have no idea what wheels are gonna fall off any second now means. It could always break between OS X releases, and I haven't tested it on 10.13 High Sierra or 10.14 Mojave, but I don't think it'll break during a given major version. – Heath Borders Jun 06 '18 at 13:15
  • 4
    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. – D. Ben Knoble Nov 01 '19 at 14:29
  • 1
    As @D.BenKnoble points out, recent versions of macOS allow mktemp -d without the -t option. The -t option still has different meanings between GNU and macOS (FreeBSD?), but at least both can run mktemp -d and produce very similar results... so, the "try GNU-mktemp OR do macOS-mktemp" pattern seems unnecessary. – Zach Young Jun 09 '22 at 19:49
  • 2
    OS X 10.11 El Capitan (shell_cmds-187) was the first release where mktemp chose tmp as template if no argument was given. – Stefan Schmidt Oct 21 '22 at 01:35
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.

Timo Tijhof
  • 7,170
Kyle Jones
  • 15,015
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. – soundly_typed 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 07: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
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)"
go2null
  • 215
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.

zatamine
  • 111