I have a script that needs to create temporary files for its work, and clean up after itself. My question is about finding the right base directory for the temporary files.

The script needs to work on multiple platforms: Git Bash (Windows), Solaris, Linux, OSX. On each platform, the preferred temp directory is expressed differently:

  • Windows: %TMP% (and possibly %TEMP%)
  • OSX: $TMPDIR
  • Linux, UNIX: supposed to be $TMPDIR but appears to be unset on multiple systems I tried

So in my script I added this boilerplate:

if test -d "$TMPDIR"; then
    :
elif test -d "$TMP"; then
    TMPDIR=$TMP
elif test -d /var/tmp; then
    TMPDIR=/var/tmp
else
    TMPDIR=/tmp
fi

This seems too tedious. Is there a better way?

  • 6
    Just use ${TMPDIR-/tmp} on Unix-likes. TMPDIR is there (by the system or administrator or user) to tell you when not to use /tmp for temporary files. – Stéphane Chazelas Dec 18 '14 at 15:02
up vote 57 down vote accepted
+100

A slightly more portable way to handle temporary files is to use mktemp. It'll create temporary files and return their paths for you. For instance:

$ mktemp
/tmp/tmp.zVNygt4o7P
$ ls /tmp/tmp.zVNygt4o7P
/tmp/tmp.zVNygt4o7P

You could use it in a script quite easily:

tmpfile=$(mktemp)
echo "Some temp. data..." > $tmpfile
rm $tmpfile

Reading the man page, you should be able to set options according to your needs. For instance:

  • -d creates a directory instead of a file.
  • -u generates a name, but does not create anything.

Using -u you could retrieve the temporary directory quite easily with...

$ tmpdir=$(dirname $(mktemp -u))

More information about mktemp is available here.

Edit regarding Mac OS X: I have never used a Mac OSX system, but according to a comment by Tyilo below, it seems like Mac OSX's mktemp requires you to provide a template (which is an optional argument on Linux). Quoting:

The template may be any file name with some number of "Xs" appended to it, for example /tmp/temp.XXXX. The trailing "Xs" are replaced with the current process number and/or a unique letter combination. The number of unique file names mktemp can return depends on the number of "Xs" provided; six "Xs" will result in mktemp selecting 1 of 56800235584 (62 ** 6) possible file names.

The man page also says that this implementation is inspired by the OpenBSD man page for mktemp. A similar divergence might therefore be observed by OpenBSD and FreeBSD users as well (see the History section).

Now, as you probably noticed, this requires you to specify a complete file path, including the temporary directory you are looking for in your question. This little problem can be handled using the -t switch. While this option seems to require an argument (prefix), it would appear that mktemp relies on $TMPDIR when necessary.

All in all, you should be able to get the same result as above using...

$ tmpdir=$(dirname $(mktemp tmp.XXXXXXXXXX -ut))

Any feedback from Mac OS X users would be greatly appreciated, as I am unable to test this solution myself.

  • The OP addressed this issue in a now-deleted comment ; I'm afraid I cannot provide an answer for systems which require gitbash to get a shell interface, hence my "slightly". I would actually be unable to tell where temp files are stored on a Windows system... – John WH Smith Dec 18 '14 at 13:06
  • 1
    ditto that. Some crazy c:\User\Name\..............\stuff. And there are probably 100 of them. Still, some linux distros are approaching the same level of madness in /var. I was just curious. It struck me as an odd way to put it is all - in my experience frustration is very portable, and convenience less so. Very good answer anyway. – mikeserv Dec 18 '14 at 13:10
  • 1
    A year or so ago I wrote a Bash script to manipulate Windows Restore Point files that works on Win2K through to WinXP (later versions handle that stuff differently). My script uses WINDOWS/Temp or Windows/Temp as the default directory for its temp files; that directory also exists on Windows 7 machines. But my script allows the user to supply an alternate temp dir on the command line. – PM 2Ring Dec 18 '14 at 13:53
  • This wouldn't work on OS X, as you have to supply a template to mktemp. – Tyilo Dec 24 '14 at 20:50
  • 1
    Since El Capitan, mktemp just works: simple mktemp returns a file, and mktemp -d returns a directory; no templates required. The manual is not updated though. – Franklin Yu Apr 30 '17 at 4:18

If you're looking for the same thing in fewer lines...

for TMPDIR in "$TMPDIR" "$TMP" /var/tmp /tmp
do
    test -d "$TMPDIR" && break
done

You could write this in one.

  • You should probably also test if it's writable. – frostschutz Dec 18 '14 at 11:20
  • @janos: And if all else fails, ask the user to supply a path for temp files, or allow them to specify one on the command line. – PM 2Ring Dec 18 '14 at 11:32

You might do:

: "${TMPDIR:=${TMP:-$(CDPATH=/var:/; cd -P tmp)}}"
cd -- "${TMPDIR:?NO TEMP DIRECTORY FOUND!}" || exit

The shell should either find an executable directory in one of the 4 alternatives or exit with a meaningful error. Still, POSIX defines the $TMPDIR variable (for XCU systems):

TMPDIR This variable shall represent a pathname of a directory made available for programs that need a place to create temporary files.

It also requires the /tmp path.

  • 1
    This is the only right answer here IMO. Dunno what it is doing sitting down the bottom with no votes... – Graeme Dec 24 '14 at 10:54
  • 2
    @Graeme: maybe because it's utterly unreadable ? – ssc Feb 6 '16 at 14:04
  • I upvoted but then retracted it-- yes, this answer is a mess, It seems to be on a useful track, but it's not at all clear what the code snippit is trying to do-- the first line is a comment and the second one is an error (in bash, anyway). Could use some work. – Don Hatch Oct 5 at 20:41
  • Oh I see, the first version of the answer was clearer (still an error on 2nd line if you actually try to run it though)... and then a series of edits completely trashed the code sample so it's now nonsense. Could use another pass or two :-) – Don Hatch Oct 5 at 20:43

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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