When I use the "trap" command in bash, the previous trap for the given signal is replaced.
Is there a way of making more than one trap fire for the same signal?
When I use the "trap" command in bash, the previous trap for the given signal is replaced.
Is there a way of making more than one trap fire for the same signal?
Simple ways to do it
Usage:
Remark : This works only has long as the handlers are function names, or simple instructions that did not contains any simple cote (simple cotes conflicts with
cut -f2 -d \'
).I didn't like having to play with these string manipulations which are confusing at the best of times, so I came up with something like this:
(obviously you can modify it for other signals)
I liked Richard Hansen's answer, but I don't care for embedded functions so an alternate is:
There's no way to have multiple handlers for the same trap, but the same handler can do multiple things.
The one thing I don't like in the various other answers doing the same thing is the use of string manipulation to get at the current trap function. There are two easy ways of doing this: arrays and arguments. Arguments is the most reliable one, but I'll show arrays first.
Arrays
When using arrays, you rely on the fact that
trap -p SIGNAL
returnstrap -- ??? SIGNAL
, so whatever is the value of???
, there are three more words in the array.Therefore you can do this:
So let's explain this. First, variable
trapDecl
is declared as an array. If you do this inside a function, it will also be local, which is convenient.Next we assign the output of
trap -p SIGNAL
to the array. To give an example, let's say you are running this after having sourced osht (unit testing for shell), and that the signal isEXIT
. The output oftrap -p EXIT
will betrap -- '_osht_cleanup' EXIT
, so thetrapDecl
assignment will be substituted like this:The parenthesis there are normal array assignment, so
trapDecl
becomes an array with four elements:trap
,--
,'_osht_cleanup'
andEXIT
.Next we extract the current handler -- that could be inlined in the next line, but for explanation's sake I assigned it to a variable first. Simplifying that line, I'm doing this:
currentHandler="${array[@]:offset:length}"
, which is the syntax used by Bash to say picklength
elements starting at elementoffset
. Since it starts counting from0
, number2
will be'_osht_cleanup'
. Next,${#trapDecl[@]}
is the number of elements insidetrapDecl
, which will be 4 in the example. You subtract 3 because there are three elements you don't want:trap
,--
andEXIT
. I don't need to use$(...)
around that expression because arithmetic expansion is already performed on theoffset
andlength
arguments.The final line performs an
eval
, which is used so that the shell will interpret the quoting from the output oftrap
. If we do parameter substitution on that line, it expands to the following in the example:Do not be confused by the double quote in the middle (
''
). Bash simply concatenates two quotes strings if they are next to each other. For example,'1'"2"'3''4'
is expanded to1234
by Bash. Or, to give a more interesting example,1" "2
is the same thing as"1 2"
. So eval takes that string and evaluates it, which is equivalent to executing this:And that will handle the quoting correctly, turning everything between
--
andEXIT
into a single parameter.To give a more complex example, I'm prepending a directory clean up to the osht handler, so my
EXIT
signal now has this:If you assign that to
trapDecl
, it will have size 6 because of the spaces on the handler. That is,'rm
is one element, and so is-fr
, instead of'rm -fr ...'
being a single element.But
currentHandler
will get all three elements (6 - 3 = 3), and the quoting will work out wheneval
is run.Arguments
Arguments just skips all the array handling part and uses
eval
up front to get the quoting right. The downside is that you replace the positional arguments on bash, so this is best done from a function. This is the code, though:The first line will set the positional arguments to the output of
trap -p SIGNAL
. Using the example from the Arrays section,$1
will betrap
,$2
will be--
,$3
will be_osht_cleanup
(no quotes!), and$4
will beEXIT
.The next line is pretty straightforward, except for
${3:+;}
. The${X:+Y}
syntax means "outputY
if the variableX
is unset or null". So it expands to;
if$3
is set, or nothing otherwise (if there was no previous handler forSIGNAL
).Here's another option:
Usage:
Technically you can't set multiple traps for the same signal, but you can add to an existing trap:
trap -p
Here is a bash function that does the above:
Example usage: