0

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?

  • 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? – Kamil Maciorowski Jul 5 '17 at 18:54
  • 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 Jul 5 '17 at 21:06
1

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"
    
  • 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? – kaligne Jul 6 '17 at 18:21
  • @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. – Kamil Maciorowski Jul 6 '17 at 18:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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