Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple Bash traps for the same signal

Tags:

bash

bash-trap

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?

like image 903
jes5199 Avatar asked Jul 26 '10 18:07

jes5199


People also ask

How do you use bash trap?

How to Use trap in Bash. A typical scenario for using the trap command is catching the SIGINT signal. This signal is sent by the system when the user interrupts the execution of the script by pressing Ctrl+C. The following example script prints the word "Test" every second until the user interrupts it with Ctrl+C.

How do you trap a Sigkill?

You can't catch SIGKILL (and SIGSTOP ), so enabling your custom handler for SIGKILL is moot. You can catch all other signals, so perhaps try to make a design around those. be default pkill will send SIGTERM , not SIGKILL , which obviously can be caught.

What does $! Mean in Linux?

$! is the process ID of the last job run in the background. $$ is the process ID of the script itself. (Both of the above are links to the Advanced Bash Scripting Guide on TDLP.)

What is trap signal?

Trap allows you to catch signals and execute code when they occur. Signals are asynchronous notifications that are sent to your script when certain events occur. Most of these notifications are for events that you hope never happen, such as an invalid memory access or a bad system call.


2 Answers

Technically you can't set multiple traps for the same signal, but you can add to an existing trap:

  1. Fetch the existing trap code using trap -p
  2. Add your command, separated by a semicolon or newline
  3. Set the trap to the result of #2

Here is a bash function that does the above:

# note: printf is used instead of echo to avoid backslash # processing and to properly handle values that begin with a '-'.  log() { printf '%s\n' "$*"; } error() { log "ERROR: $*" >&2; } fatal() { error "$@"; exit 1; }  # appends a command to a trap # # - 1st arg:  code to add # - remaining args:  names of traps to modify # trap_add() {     trap_add_cmd=$1; shift || fatal "${FUNCNAME} usage error"     for trap_add_name in "$@"; do         trap -- "$(             # helper fn to get existing trap command from output             # of trap -p             extract_trap_cmd() { printf '%s\n' "$3"; }             # print existing trap command with newline             eval "extract_trap_cmd $(trap -p "${trap_add_name}")"             # print the new trap command             printf '%s\n' "${trap_add_cmd}"         )" "${trap_add_name}" \             || fatal "unable to add to trap ${trap_add_name}"     done } # set the trace attribute for the above function.  this is # required to modify DEBUG or RETURN traps because functions don't # inherit them unless the trace attribute is set declare -f -t trap_add 

Example usage:

trap_add 'echo "in trap DEBUG"' DEBUG 
like image 183
Richard Hansen Avatar answered Sep 21 '22 14:09

Richard Hansen


Edit:

It appears that I misread the question. The answer is simple:

handler1 () { do_something; } handler2 () { do_something_else; } handler3 () { handler1; handler2; }  trap handler3 SIGNAL1 SIGNAL2 ... 

Original:

Just list multiple signals at the end of the command:

trap function-name SIGNAL1 SIGNAL2 SIGNAL3 ... 

You can find the function associated with a particular signal using trap -p:

trap -p SIGINT 

Note that it lists each signal separately even if they're handled by the same function.

You can add an additional signal given a known one by doing this:

eval "$(trap -p SIGUSR1) SIGUSR2" 

This works even if there are other additional signals being processed by the same function. In other words, let's say a function was already handling three signals - you could add two more just by referring to one existing one and appending two more (where only one is shown above just inside the closing quotes).

If you're using Bash >= 3.2, you can do something like this to extract the function given a signal. Note that it's not completely robust because other single quotes could appear.

[[ $(trap -p SIGUSR1) =~ trap\ --\ \'([^\047]*)\'.* ]] function_name=${BASH_REMATCH[1]} 

Then you could rebuild your trap command from scratch if you needed to using the function name, etc.

like image 28
Dennis Williamson Avatar answered Sep 22 '22 14:09

Dennis Williamson