3

I'm wondering if something like this is possible.

function get_temp_dir() {
    local tmp_dir=$(mktemp -d)
    trap "{
           rm -r $tmp_dir
       }" EXIT
    echo $tmp_dir
}

temp=$(get_temp_dir)

# I'd like to $temp for the duration of this script, and deleted
# when this current scope ends, not when the function scope ends.

I've seen other scripts achieve the same effect using global arrays with a single trap function that enumerates the array and does something with it. I'd like to avoid that if I can.

I'm also aware that I could just create one tmp directory, and create multiple tmp directories within said directory. However, I'd like to use this approach because it is broadly usable for other things, such as mount/unmount.

Any ideas? Thanks in advance.

edit: So, TRAP does get called when the shell script ends, however, in my original question, I was using a subshell ($()). I got it working after reformatting my code to this:

#!/usr/bin/env bash

function get_temp_dir() {
    local tmp_dir=$(mktemp -d)
    trap "{
           rm -r $tmp_dir
       }" EXIT
    retval=$tmp_dir
}

get_temp_dir
tmp_dir=$retval

ls $tmp_dir
11
  • I don't understand what it is you're looking for. A cleaner way to remove temporary files?
    – DopeGhoti
    May 11, 2018 at 17:56
  • Yes. I have to create many temporary directories. I'd like to avoid boilerplate for cleanup code, and have one single function that gives me temp directory, and auto cleans it up on exit.
    – Paul Knopf
    May 11, 2018 at 17:57
  • Doesn't an EXIT trap fire when the shell exits, not when the function returns? Isn't that what you want, apart from the fact that you can only have one EXIT trap at a time? The workaround for that could indeed be a single trap that runs any and all necessary cleanup commands that could be stored in an array or such, just as you say.
    – ilkkachu
    May 11, 2018 at 17:57
  • 1
    Ah, I see, $(get_temp_dir) creates another shell process, trap functions get executed when exited.
    – Paul Knopf
    May 11, 2018 at 18:16
  • 1
    @PaulKnopf, ah, sorry, in your case, the subshell exits, so the trap runs. bash -c 'set_trap() { trap "echo trap >&2" EXIT; }; : $(set_trap); echo end;' prints trap and then end. Which sort of means that you can't set a useful trap in a function you call from $() (and neither could you add the cleanup code to an array, since that also wouldn't show up in the main shell.)
    – ilkkachu
    May 11, 2018 at 18:16

2 Answers 2

4

I think you're looking for the RETURN signal:

[...] If a SIGNAL_SPEC is EXIT (0) ARG is executed on exit from the shell.
[...] If a SIGNAL_SPEC is RETURN, ARG is executed each time a shell function or a script run by the . or source builtins finishes executing.

Example:

$ bash
$ trap "echo shell exiting" EXIT
$ fn() { trap "echo function exiting" RETURN; }
$ fn
function exiting
$ (fn)
function exiting
$ value=$(fn); echo "$value"
function exiting
$ exit
shell exiting

and, germaine to the question:

$ f2() { 
    local tmp=$(mktemp)
    trap 'rm "$tmp"' RETURN
    echo "$tmp"
    date >> "$tmp"
    cat "$tmp"
}
$ f2
/tmp/tmp.MHpI20X0a1
Fri May 11 14:29:01 EDT 2018
$ ls -l /tmp/tmp.MHpI20X0a1
ls: cannot access '/tmp/tmp.MHpI20X0a1': No such file or directory
1
  • Thanks. I now see how to create a TRAP for a function scope. My original script in original question was behaving like a function scope TRAP because I was executing with $(). That was the crux of my issue. I wanted it to be a global scope, when the shell exits.
    – Paul Knopf
    May 11, 2018 at 18:27
0

In my scripts, I often do something like this at the very beginning in cases where I'll be writing temporary files:

scratch="$(mktemp -d)"
trap 'rm -fr "$scratch"' EXIT

Any actual files or directories can be written by name (e. g. mkdir $scratch/foo; touch $scratch/foo/bar) over the course of the script, but housekeeping is all taken care of at once upon script termination with the trap.

3
  • This solution would work exactly as the question was asked. However, I'd like to use the solution for mounts/unmounts as well. I have further clarified my question.
    – Paul Knopf
    May 11, 2018 at 18:03
  • Handling mounts on EXIT is somewhat difficult. What do you do if umount fails because there is an open file handle? We're EXITing, so it has to be immediate.
    – DopeGhoti
    May 11, 2018 at 18:10
  • Agreed. In my case, errors won't happen, since I'm creating tmp dir and mounting newly created device. Arch does something like this with arch-chroot, and it hasn't caused me any issues. See here: gist.github.com/systemdarena/…
    – Paul Knopf
    May 11, 2018 at 18:12

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .