How to reliably remove all temporary files created in a script?

6,376

Subshells inherit the environment or some part of it. It looks like when you execute a function in a subshell, you don't need your variables to be exported (compare: No need for export when running functions in subshell). There may be some cases when you need to export but let's concentrate on your example.

Your approach is flawed and will be hard to maintain because inheritance goes one way. Every (mfunc) inherits tmp_tmp_files and works with its own copy of the array. There's no way it could modify the original tmp_tmp_files used in the main script. Therefore the main script cannot clean everything, it just doesn't possess enough information.

If you choose to clean from the subshelled function then you will need to pay attention and tell apart the temporary files of the function from the temporary files of the main script. Prematurely removing the latter may cause your script to fail.

Alternative approach: a temporary directory

  1. Instead of creating an array, create a directory and remember its path. Do it once in the beginning and export if you need:

    tempdir=$(mktemp -d mscript.XXXXXXXXXX)
    
  2. Create every temporary file within the temporary directory, like this:

    tempfile=$(mktemp -p "$tempdir")
    
  3. At the end remove the entire directory, no matter whether the particular files were created by the main script or by a subshell, or even by some stray third-party program. You may use a trap to perform this step. Simply:

    rm -r "$tempdir"
    
Share:
6,376

Related videos on Youtube

kaligne
Author by

kaligne

Updated on September 18, 2022

Comments

  • kaligne
    kaligne over 1 year

    Is it possible to trap the exit/return of a function? For a program I could do

    trap -- "clean_this" EXIT
    

    That would execute the function clean_this when the program exits. I would like to do something of the sort when exiting from a function.

    function myfunc() {
      echo "I'm a function"
    }
    
    myfunc &
    wait $!
    

    I execute the function in a subshell, and I'd like to trap it's exit/return. Is that possible?


    EDIT1

    Here is my purpose

    I have one script supposed to manage temporary files:

    cat tempfiles.sh
    
    ## List of temp files
    tmp_tmp_files=()
    
    ## Adds a file to the list of temp files
    function tmp_add_file() {
      tmp_tmp_files+=("$1")
    }
    
    ## Resets the list of temp files
    function tmp_reset_files() {
      tmp_tmp_files=()
    }
    
    ## Removes the list of temp files 
    function tmp_rm_all() {
      rm -f "${tmp_tmp_files[@]}"
    }
    
    ## Removes all temp files on exit and sigint
    trap "tmp_rm_all" EXIT SIGINT
    

    Here is my main script:

    cat mscript.sh
    
    source tempfiles.sh
    
    ## ## Creates a temp file and writes in it
    mfunc() {
      local tempfile=$(mktemp)
      tmp_add_file $tempfile
      echo "something" >> $tempfile
      echo "($BASHPID) - tempfiles: ${tmp_tmp_files[@]}"
    }
    
    ## Creates a temp file in main shell
    mfunc
    
    ## Creates a temp file in a subshell
    (mfunc)
    

    I call the main script:

    $ bash mscript.sh 
    (92250) - tempfiles: /var/folders/9k/h6hn75090_n8z0kythwmwqp96_0t2m/T/tmp.oRlUxEBj
    (92254) - tempfiles: /var/folders/9k/h6hn75090_n8z0kythwmwqp96_0t2m/T/tmp.oRlUxEBj /var/folders/9k/h6hn75090_n8z0kythwmwqp96_0t2m/T/tmp.s1iIvtpq
    

    I check the temp files:

    $ cat /var/folders/9k/h6hn75090_n8z0kythwmwqp96_0t2m/T/tmp.oRlUxEBj
    cat: /var/folders/9k/h6hn75090_n8z0kythwmwqp96_0t2m/T/tmp.oRlUxEBj: No such file or directory
    
    $ cat /var/folders/9k/h6hn75090_n8z0kythwmwqp96_0t2m/T/tmp.s1iIvtpq 
    something
    

    The temp files declared in the subshell are lost from the list when exiting the program. I would like to have them removed ideally at the end of the function. Either I have to specifically remove them before leaving the function, it is easy and it costs me one more line:

    mfunc() {
      local tempfile=$(mktemp)
      tmp_add_file $tempfile1
      echo "something" >> $tempfile
      echo "tempfiles: ${tmp_tmp_files[@]}"
      ## Process things...
      rm $tempfile1
    }
    

    But I'd like to know if there is an elegant way to have them (the temp files created in the subshells) removed automatically, like I do with traps when exiting the program.

    So my question is: is it possible to do it? What could be some alternatives?

    • Kamil Maciorowski
      Kamil Maciorowski almost 7 years
      What are you trying to achieve? Function put to background can call another function, so let myfunc call clean_this at the very end. Or invoke them like { myfunc ; clean_this ; } &. Do you really need a trap?
    • kaligne
      kaligne almost 7 years
      Thanks for replying. Here I edited my question and wrote my purpose. Well surely I can avoid traps, but since I started using them, if I can do what I want in an easy way... Why not
  • kaligne
    kaligne almost 7 years
    thanks that's what I do for now. So, I take it there is no way to trigger anything as soon as a function stops. Right?
  • Kamil Maciorowski
    Kamil Maciorowski almost 7 years
    @user3298319 I don't know for sure. It so happened your question was the XY problem and I answered to solve your actual problem. I also changed the title, so now it's not useful for users interested in traps; it's useful in the topic of temporary files. My point is: this question has evolved. If you want to research traps, you may ask another question and explain there that you're asking out of curiosity. This may not be well received though. The site concentrates on solving practical problems and users tend to hand you proper tools as I did.