Bash traps, capture and pass them as arguments on same function

1.3k views Asked by At

I'm developing a script which manage some traps. At the beginning I only managed INT and SIGTSTP with this code and it works very well:

#!/bin/bash
function capture_traps() {
    echo -e "\nDoing something on exit"
    exit 1
}

trap capture_traps INT
trap capture_traps SIGTSTP
read -p "Script do its stuff here and we use read for the example we pause for time to generate trap event"
exit 0

Then I tried to add the new traps I want to manage, which are SIGINT and SIGHUP. In first instance I did this (which is working):

#!/bin/bash
function capture_traps() {
    echo -e "\nDoing something on exit"
    exit 1
}

trap capture_traps INT
trap capture_traps SIGTSTP
trap capture_traps SIGINT
trap capture_traps SIGHUP
read -p "Script do its stuff here and we use read for the example we pause for time to generate trap event"
exit 0

Then, I decided to do different stuff on exit depending of the trap and I don't want to create different functions for each one. I know in bash you can loop over arguments on a function using for item in $@; do nomenclature, so I tried, but it seems is not working trying to differentiate the kind of trap. I made this code which is not working.

#!/bin/bash
function capture_traps() {

    for item in $@; do
        case ${item} in
            INT|SIGTSTP)
                echo -e "\nDoing something on exit"
            ;;
            SIGINT|SIGHUP)
                echo -e "\nDoing another thing even more awesome"
            ;;
        esac
    done
    exit 1
}

trap capture_traps INT SIGTSTP SIGINT SIGHUP
read -p "Script do its stuff here and we use read for the example we pause for time to generate trap event"
exit 0

Any help? There must be a way to improve my code using only one function for all traps, but I don't know how...

1

There are 1 answers

0
cxw On BEST ANSWER

You can pass arguments to your trap handler:

#!/bin/bash
function capture_traps() {

    #for item in $@; do
    case "$1" in
        INT|SIGTSTP)
            echo -e "\nDoing something on exit"
        ;;
        SIGINT|SIGHUP)
            echo -e "\nDoing another thing even more awesome"
        ;;
    esac
    #done
    exit 1
}

for f in INT SIGTSTP SIGINT SIGHUP ; do
    trap "capture_traps $f" "$f"
done

read -p "Script do its stuff here and we use read for the example we pause for time to generate trap event"
exit 0

In the above code (tested on cygwin, bash 4.3.46), capture_traps takes one parameter: the name of the trap. That is $1 in capture_traps. Since it only gets one trap at a time, it doesn't need a loop.

To set up the traps, the loop iterates over each trap you want (INT SIGTSTP...) and runs

trap "capture_traps $f" "$f"

The first argument can be more general than a function name: it is

shell code ... to be read and executed whenever the shell receives a signal or another event

per the wiki. Therefore, the command capture_traps $f (with the trap name substituted in) will run on that particular trap (the second argument, "$f". Et voila!

... just realized I should have checked for duplicates first :) . Here's another answer and still another.