How to create a temporary directory?
Asked Answered
H

6

329

I used to create a tempfile, delete it and recreate it as a directory:

temp=`tempfile`
rm -f $temp
  # <breakpoint>
mkdir $temp

The problem is, when it runs to the <breakpoint>, there happens to be another program that wants to do the same thing, which mkdir-ed a temp dir with the same name, will cause the failure of this program.

How should I make a temp directory instead of a file?

Halfwitted answered 8/1, 2011 at 2:28 Comment(1)
Git Bash for Windows does not have tempfile, but it does have mktemp.Shevat
P
469

Use mktemp -d. It creates a temporary directory with a random name and makes sure that file doesn't already exist. You need to remember to delete the directory after using it though.

Platon answered 8/1, 2011 at 2:30 Comment(7)
I had to use mktemp -d -t <prefix>Bryna
This is a OS X vs Linux thing. See this question for a version that works on both: unix.stackexchange.com/questions/30091/…Zenaidazenana
Also, see the answer below by Ortwin, as that makes sure cleanup is done as well.Foley
Why do you say "You need to remember to delete the directory after using it though."? Doesn't that kinda defeat the purpose of using a temp directory?Batwing
@M.K.Safi I think the main purpose is to create an empty directory where you can store temporary files without cluttering up other parts of the filesystem. You get to choose if/when you want to delete it. If it creates it in /tmp, then it should be cleared when the system restarts anyway.Eckmann
@Eckmann "... it should be cleared when the system restarts anyway", true, though that could be weeks, months or even years away - best clean-up to avoid space/clutter issuesMadelenemadelin
@Madelenemadelin fair enough, and it's worth saying that some environments like WSL may never automatically clear out /tmp/, but my point was just that even if you need to delete it afterwards, it doesn't undermine the whole purpose of it.Eckmann
B
113

For a more robust solution i use something like the following. That way the temp dir will always be deleted after the script exits.

The cleanup function is executed on the EXIT signal. That guarantees that the cleanup function is always called, even if the script aborts somewhere.

#!/bin/bash    

# the directory of the script
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

# the temp directory used, within $DIR
# omit the -p parameter to create a temporal directory in the default location
WORK_DIR=`mktemp -d -p "$DIR"`

# check if tmp dir was created
if [[ ! "$WORK_DIR" || ! -d "$WORK_DIR" ]]; then
  echo "Could not create temp dir"
  exit 1
fi

# deletes the temp directory
function cleanup {      
  rm -rf "$WORK_DIR"
  echo "Deleted temp working directory $WORK_DIR"
}

# register the cleanup function to be called on the EXIT signal
trap cleanup EXIT

# implementation of script starts here
...

Directory of bash script from here.

Bash traps.

Brozak answered 8/1, 2016 at 11:35 Comment(11)
FreeBSD Caution! mktemp on FreeBSD doesn't have -p option, and cleanup will rm -rf your current directory!Crabtree
Good point, updated the script to check if the temp dir could be created.Brozak
@Crabtree really? if mktemp fails, WORK_DIR will be empty, meaning the command would just be rm -rf with no argument. I don't use FreeBSD but I'd be pretty surprised if rm -rf was equivalent to rm -rf .Toilette
@Toilette yes, it seems odd to me now too - it shouldn't be a really big problem. I might have tweaked an old version of this script so that a path to temporary directory was calculated relatively to current directory, resulting in <s>extinction of mankind</s> current directory removal.Crabtree
I'll give you a -1 for safety reasons with that rm -rf thing and the trap, sorry. You are too worried in deleting a temporary directory. Not worth the risk you are causing.Tseng
@Tseng it all depends on the use case. We use it a lot in build scripts and it works just fine there. And yes i am worried about the temp files on our build server.Brozak
To make it better, you can avoid an empty directory or at least contain the problem within a directory using a solution where you do: 1. TMPWORKDIR=$(basename 'mktemp -d -p /tmp/git/') and then 2. rmdir /tmp/git/"${TMPWORKDIR}". If the variable is empty now, you will still fall back to /tmp/git/ not to the whole system. Consider something like this in the answer and I'll gladly agree. ;)Tseng
@Tseng I was about to ask the same, why use current dir of script when there's the /tmp dir you could use. Also one level deeper, your '/tmp/git` will prevent falling back to /tmp accidentally and potentially force-removing files still in use by others.Cipher
@Tseng the script will exit if the variable is empty before reaching the trap, or do i miss something?Brozak
@Cipher we use it to keep the logs/files/whatever close to the test case. It is/can be bothersome to find logs. (use `trap - EXIT´ to disable the trap if eg. a test failes)Brozak
@OrtwinAngermeier I guess that makes sense, but wouldn't the logs be deleted when it exits? That would make them * really * hard to find.Cipher
G
85

My favorite one-liner for this is

cd $(mktemp -d)
Grotius answered 4/2, 2014 at 21:46 Comment(7)
and rm $(pwd)? :PContour
also useful: pushd $(mktemp -d) ... popdStretch
@ArranCudbard-Bell should be rm -r $(pwd)Cholera
Depending on what you put in the temp directory, you might might need rm -rf $(pwd) to remove it.Indre
@Cholera Frankly, I'd be very cautious of using rm -r $(pwd). Consider the possibility that temporary directory creation fails for whatever reason (maybe the /tmp filesystem is full or has been remounted read only due to an error?); then cd $(mktemp -d) will evaluate to cd which changes to the user's home directory, which would subsequently be deleted.Communitarian
It may be safe with if pushd $(mktemp -d || echo BADMPDIR); then ........ ; rm -r $(pwd); popd; fiMindi
For bash, the current working dir (pwd) changed in the parentheses will be restored outside the parentheses. So a convenient way is: tmp=$(mktemp -d); (cd $tmp; ...)Halfwitted
A
23

The following snippet will safely create and then clean up a temporary directory.

The first trap line executes exit 1 command when any of the specified signals is received. The second trap line removes the $TEMPD on program's exit (both normal and abnormal). We initialize these traps after we check that mkdir -d succeeded to avoid accidentally executing the exit trap with $TEMPD in an unknown state.

#!/bin/bash

# set -x # un-comment to see what's going on when you run the script

# Create a temporary directory and store its name in a variable.
TEMPD=$(mktemp -d)

# Exit if the temp directory wasn't created successfully.
if [ ! -e "$TEMPD" ]; then
    >&2 echo "Failed to create temp directory"
    exit 1
fi

# Make sure the temp directory gets removed on script exit.
trap "exit 1"           HUP INT PIPE QUIT TERM
trap 'rm -rf "$TEMPD"'  EXIT
Azaria answered 30/10, 2018 at 11:45 Comment(4)
While this is an interesting solution for the error handling, a bit more explanation of the advantages and possible shortcomings would be nice.Strawboard
1.) -d checks for directories. 2.) Termination is already the default for those signals.Lynnelynnea
Don't suggest TMPDIR as variable name here, as that is typically the system's temporary directory. en.wikipedia.org/wiki/TMPDIRLaurenalaurence
Thanks @spawn, I renamed the TMPDIR variable.Azaria
V
3

Here is a simple explanation about how to create a temp dir using templates.

  1. Creates a temporary file or directory, safely, and prints its name.
  2. TEMPLATE must contain at least 3 consecutive 'X's in last component.
  3. If TEMPLATE is not specified, it will use tmp.XXXXXXXXXX
  4. directories created are u+rwx, minus umask restrictions.

PARENT_DIR=./temp_dirs # (optional) specify a dir for your tempdirs
mkdir $PARENT_DIR

TEMPLATE_PREFIX='tmp' # prefix of your new tempdir template
TEMPLATE_RANDOM='XXXX' # Increase the Xs for more random characters
TEMPLATE=${PARENT_DIR}/${TEMPLATE_PREFIX}.${TEMPLATE_RANDOM}

# create the tempdir using your custom $TEMPLATE, which may include
# a path such as a parent dir, and assign the new path to a var
NEW_TEMP_DIR_PATH=$(mktemp -d $TEMPLATE)
echo $NEW_TEMP_DIR_PATH

# create the tempdir in parent dir, using default template
# 'tmp.XXXXXXXXXX' and assign the new path to a var
NEW_TEMP_DIR_PATH=$(mktemp -p $PARENT_DIR)
echo $NEW_TEMP_DIR_PATH

# create a tempdir in your systems default tmp path e.g. /tmp 
# using the default template 'tmp.XXXXXXXXXX' and assign path to var
NEW_TEMP_DIR_PATH=$(mktemp -d)
echo $NEW_TEMP_DIR_PATH

# Do whatever you want with your generated temp dir and var holding its path

Villar answered 25/1, 2021 at 3:47 Comment(0)
R
3

I need the following features:

  • Put all temp files into a single directory with a specific namespace for reusing.
  • Create temp files with filename prefix and suffix (extension).

With bash script on macOS:


$ namespace="com.namespace.mktemp"

# find directory for reusing
$ ls -d "${TMPDIR}${namespace}"*

# create directory if not exists
$ mktemp -d -t "$namespace"
/var/folders/s_/.../T/com.namespace.mktemp.HjqGT6w2

# create tempfile with directory name and file prefix
$ mktemp -t "com.namespace.mktemp.HjqGT6w2/file-prefix"
/var/folders/s_/.../T/com.namespace.mktemp.HjqGT6w2/file-prefix.sZDvjo14

# add suffix - `mktemp` on macOS does not support `--suffix`
mv "/var/folders/s_/.../file-prefix.sZDvjo14" "/var/folders/s_/.../file-prefix.sZDvjo14.txt"

The gmktemp (brew install coreutils) is a little different:

  • supports --suffix and --tmpdir
  • Xs are required in template and prefix
  • template should not contain directory, set TMPDIR instead

$ namespace="com.namespace.gmktemp"

# create directory if not exists
$ gmktemp -d -t "$namespace.XXXXXXXX"
/var/folders/s_/.../T/com.namespace.gmktemp.BjFtIAyZ

# set TMPDIR
TMPDIR="/var/folders/s_/.../T/com.namespace.gmktemp.BjFtIAyZ"

# create tempfile with directory name and file prefix
$ gmktemp --suffix=".txt" -t "prefix.XXXXXXXX"
/var/folders/s_/.../T/com.namespace.gmktemp.BjFtIAyZ/prefix.LWHj0G95.txt

Ramsdell answered 24/11, 2022 at 10:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.