Removing created temp files in unexpected bash exit

BashExitTemporary Files

Bash Problem Overview


I am creating temporary files from a bash script. I am deleting them at the end of the processing, but since the script is running for quite a long time, if I kill it or simply CTRL-C during the run, the temp files are not deleted.
Is there a way I can catch those events and clean-up the files before the execution ends?

Also, is there some kind of best practice for the naming and location of those temp files?
I'm currently not sure between using:

TMP1=`mktemp -p /tmp`
TMP2=`mktemp -p /tmp`
...

and

TMP1=/tmp/`basename $0`1.$$
TMP2=/tmp/`basename $0`2.$$
...

Or maybe is there some better solutions?

Bash Solutions


Solution 1 - Bash

I usually create a directory in which to place all my temporary files, and then immediately after, create an EXIT handler to clean up this directory when the script exits.

MYTMPDIR="$(mktemp -d)"
trap 'rm -rf -- "$MYTMPDIR"' EXIT

If you put all your temporary files under $MYTMPDIR, then they will all be deleted when your script exits in most circumstances. Killing a process with SIGKILL (kill -9) kills the process right away though, so your EXIT handler won't run in that case.

Solution 2 - Bash

You could set a "trap" to execute on exit or on a control-c to clean up.

trap '{ rm -f -- "$LOCKFILE"; }' EXIT

Alternatively, one of my favourite unix-isms is to open a file, and then delete it while you still have it open. The file stays on the file system and you can read and write it, but as soon as your program exits, the file goes away. Not sure how you'd do that in bash, though.

BTW: One argument I'll give in favour of mktemp instead of using your own solution: if the user anticipates your program is going to create huge temporary files, he might want set TMPDIR to somewhere bigger, like /var/tmp. mktemp recognizes that, your hand-rolled solution (second option) doesn't. I frequently use TMPDIR=/var/tmp gvim -d foo bar, for instance.

Solution 3 - Bash

You want to use the trap command to handle exiting the script or signals like CTRL-C. See the Greg's Wiki for details.

For your tempfiles, using basename $0 is a good idea, as well as providing a template that provides room for enough temp files:

tempfile() {
    tempprefix=$(basename "$0")
    mktemp /tmp/${tempprefix}.XXXXXX
}

TMP1=$(tempfile)
TMP2=$(tempfile)

trap 'rm -f $TMP1 $TMP2' EXIT

Solution 4 - Bash

Just keep in mind that choosen answer is bashism, which means solution as

trap "{ rm -f $LOCKFILE }" EXIT

would work only in bash (it will not catch Ctrl+c if shell is dash or classic sh), but if you want compatibility then you still need to enumerate all signals that you want to trap.

Also keep in mind that when script exits the trap for signal "0"(aka EXIT) is always performed resulting in double execution of trap command.

That the reason not to stack all signals in one line if there is EXIT signal.

To better understand it look at following script that will work across different systems without changes:

#!/bin/sh

on_exit() {
  echo 'Cleaning up...(remove tmp files, etc)'
}

on_preExit() {
  echo
  echo 'Exiting...' # Runs just before actual exit,
                    # shell will execute EXIT(0) after finishing this function
                    # that we hook also in on_exit function
  exit 2
}


trap on_exit EXIT                           # EXIT = 0
trap on_preExit HUP INT QUIT TERM STOP PWR  # 1 2 3 15 30


sleep 3 # some actual code...

exit 

This solution will give you more control since you can run some of your code on occurrence of actual signal just before final exit (preExit function) and if it needed you can run some code at actual EXIT signal (final stage of exit)

Solution 5 - Bash

I cannot believe that so many people assume that a file name will not contain a space. The world will crash if $TMPDIR is ever assigned to "temporary directory".

zTemp=$(mktemp --tmpdir "$(basename "$0")-XXX.ps")
trap "rm -f ${zTemp@Q}" EXIT

Spaces and other special characters like single quotes and carriage returns in file names should be considered in code as a requirement of decent programming habit.

Solution 6 - Bash

The alternative of using a predictable file name with $$ is a gaping security hole and you should never, ever, ever think about using it. Even if it is just a simple personal script on your single user PC. It is a very bad habit you should not obtain. BugTraq is full of "insecure temp file" incidents. See here, here and here for more information on the security aspect of temp files.

I was initially thinking of quoting the insecure TMP1 and TMP2 assignments, but on second thought that would probably not be a good idea.

Solution 7 - Bash

I prefer using tempfile which creates a file in /tmp in the safe manner and you do not have to worry about its naming:

tmp=$(tempfile -s "your_sufix")
trap "rm -f '$tmp'" exit

Solution 8 - Bash

You don't have to bother removing those tmp files created with mktemp. They will be deleted anyway later.

Use mktemp if you can as it generates more unique files then '$$' prefix. And it looks like more cross platform way to create temp files then explicitly put them into /tmp.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionskinpView Question on Stackoverflow
Solution 1 - BashChris AtLeeView Answer on Stackoverflow
Solution 2 - BashPaul TomblinView Answer on Stackoverflow
Solution 3 - BashBrian CampbellView Answer on Stackoverflow
Solution 4 - BashAlexView Answer on Stackoverflow
Solution 5 - BashPaulView Answer on Stackoverflow
Solution 6 - BashhlovdalView Answer on Stackoverflow
Solution 7 - BashRuslan KabalinView Answer on Stackoverflow
Solution 8 - BashMykola GolubyevView Answer on Stackoverflow