Dowemo


Question:

I am looking for a way to clean up the mess when my top-level script exits.

Especially if I want to use set -e, I wish the background process would die when the script exits.


Best Answer:


The trap 'kill 0' SIGINT SIGTERM EXIT solution described in @tokland's answer is really nice, but latest Bash crashes with a segmantation fault when using it. That's because Bash, starting from v. 4.3, allows trap recursion, which becomes infinite in this case:

  • shell process recieves SIGINT or SIGTERM or EXIT;
  • the signal gets trapped, executing kill 0, which sends SIGTERM to all processes in the group, including the shell itself;
  • go to 1 :)
  • This can be worked around by manually de-registering the trap:

    trap 'trap - SIGTERM && kill 0' SIGINT SIGTERM EXIT
    
    
    

    The more fancy way, that allows to print the recieved signal and avoids "Terminated:" messages:

    #!/usr/bin/env bash
    
    
    
    trap_with_arg() { # from https://stackoverflow.com/a/2183063/804678
    
    
      local func="$1"; shift
    
    
      for sig in "$@"; do
    
    
        trap "$func $sig" "$sig"
    
    
      done
    
    
    }
    
    
    
    stop() {
    
    
      trap - SIGINT EXIT
    
    
      printf 'n%sn' "recieved $1, killing children"
    
    
      kill -s SIGINT 0
    
    
    }
    
    
    
    trap_with_arg 'stop' EXIT SIGINT SIGTERM SIGHUP
    
    
    
    { i=0; while (( ++i )); do sleep 0.5 && echo "a: $i"; done } &
    
    
    { i=0; while (( ++i )); do sleep 0.6 && echo "b: $i"; done } &
    
    
    
    while true; do read; done
    
    
    

    UPD: added minimal example; improved stop function to aviod de-trapping unnecessary signals and to hide "Terminated:" messages from the output. Thanks Trevor Boyd Smith for the suggestions!




    Copyright © 2011 Dowemo All rights reserved.    Creative Commons   AboutUs