Home > Blockchain >  bash trap not working when script is sourced
bash trap not working when script is sourced

Time:10-19

sorry I cannot figure out what is wrong with the following. When I hit ctrl-c in a script the trap works, but does not when the script is source executed.

Example (test2.sh):

#!/bin/bash

ctrl_c()
{                                                 
  echo "user canceled."                                    
}                                                          

trap ctrl_c INT

sleep 500

trap - INT

The following happens:

[root@localhost ~]# ./test2
^Cuser canceled.

[root@localhost ~]# . test2
^C

This is RHEL 7, Bash 4.2, but the same problem is also happening in other Linux distributions and also Bash 4.4.

Please help. Thanks!

CodePudding user response:

I seem to recall that interactive shells (which is where you're doing the trap because you're sourcing it rather than running it asynchronously) have special handling for SIGINT. They capture it themselves so that process control calls can be interrupted.

I suspect that's getting in the way of what you're trying to do.


UPDATE: Just had a look at the bash man-page, the SIGNALS section there is a little vague but it does seem to confirm my recollection re the special handling in interactive shells.

SIGNALS

When bash is interactive, in the absence of any traps, it ignores SIGTERM (so that kill 0 does not kill an interactive shell), and SIGINT is caught and handled (so that the wait builtin is interruptible). In all cases, bash ignores SIGQUIT.

CodePudding user response:

So here is how I solved my problem. Maybe it's not the best or most elegant way but it seems to work just fine.

The trick is to run the procedure in a background task (child process), then wait for the process to finish. While the background process ($!) is running, trap INT will work and do the cleanup. Since I need to source some commands I write them into a file and then source execute the files in the main script. These file, however, are cleaned upon Ctrl-C and hence the script ends.

For example:

# scan.sh

scan.sh, not sourced. Aborting...

# source scan.sh

 [|] Working...^C
scan.sh: waiting for process 22390 to terminate...
scan.sh: user canceled.
[1]   Interrupt               doit $f_menu $f_alias

I did not find a working solution to eliminate the "Interrupted" message, but that's not really a concern.

#!/bin/bash
....
# Preliminary options and environment. 
save_monitor=$(set  o | grep -w monitor)
set  o monitor # Turn off monitor mode.
save_history=$(set  o | grep -w history)
set  o history # Turn off command line history.
save_extglob=$(shopt -p extglob)
shopt -s extglob
f_menu=/tmp/1_$$
f_alias=/tmp/2_$$ 
iam=scan.sh                                        

cleanup()
{ # Requires $1 (f_menu) $2 (f_alias) args.
  # Restore shell the previous shell environment.  
  unset i f_pid f_menu f_alias aver iam list aname line sourced
  unset spinner doit cleanup ctrl_c
  trap - INT
  \rm -f $1 $2 # Don't use rm alias.
  eval "$save_extglob"
  eval "$save_monitor"
  eval "$save_history"
  unset save_extglob save_history save_monitor
}
 
# Exit if the script was not sourced.
$(return >/dev/null 2>&1) && sourced=1 || unset sourced
[[ "$sourced" ]] || { echo; echo "$iam, not sourced. Aborting..."; exit 1; } 

ctrl_c()
{ # Requried: f_pid f_quit
  # Optional: f_menu $f_alias
  kill -TERM $f_pid
  while (( f_pid )); do
    echo; echo "$iam: waiting for process $f_pid to terminate..."
    sleep 2
    [[ $(kill -0 $f_pid 2>&-) ]] || f_pid=
  done
  echo "$iam: user canceled."
  cleanup $f_menu $f_alias
}

trap ctrl_c INT

spinner()
{ 
  # Optional: $1
  local disp
  disp='|/-\'
  while kill -0 $! 2>&-; do
    printf " [%c] %s..." "$disp" "$1"
    disp=${disp#?}${disp%%???}
    sleep .1
    printf "\r"
  done
}

doit()
{ 
  # Requires $1 (f_menu) $2 (f_alias) args. 
 
  .....

}

echo
doit $f_menu $f_alias &
f_pid=$!; spinner "Working"; wait $f_pid

# Following will no longer work after user hit control-c.
source $f_alias 2>&-
cleanup $f_menu $f_alias 2>&-

# end
  •  Tags:  
  • bash
  • Related