I have the following process execution structure:
script0.sh
script1.sh
script2.sh
script3.sh
Meaning:
- script0.sh executes script1.sh.
- script1.sh executes script2.sh and script3.sh.
Ideally, I want to have a single trap handler in script1.sh, which would perform a cleanup and exit.
For some reason, it doesn't work.
I also tried having a trap handlers in all my scripts, but only the trap handler in script0.sh is being called.
I tried using the trap handler in script0.sh to kill the child process (i.e. script1.sh) but the process hangs until it completes.
I used the command 'trap -p' and noticed that only script0.sh has trap handlers, although that I registered trap handlers for all the scripts.
Note:
- script2.sh is actually 'rsync', the remaining scripts are my own.
- script1.sh, script2.sh and script3.sh were sent to background using '&' operator.
- I perform 'wait' for every process that is sent to background.
I created the script files which reproduce this issue:
script0.sh:
#!/usr/bin/env bash
function cleanup_script0()
{
echo "SIGINT in script0.sh"
kill -s SIGINT $script1_pid
wait $script1_pid
}
trap cleanup_script0 SIGINT
./script1.sh &
script1_pid=$!
wait $script1_pid
script1.sh:
#!/usr/bin/env bash
function cleanup_script1()
{
echo "SIGINT in script1.sh"
kill -s SIGINT $script2_pid
wait $script2_pid
kill -s SIGINT $script3_pid
wait $script3_pid
}
trap cleanup_script1 SIGINT
rsync_file_to_copy="$( mktemp )"
dd if=/dev/urandom of="$rsync_file_to_copy" bs=1M count=1
rsync --timeout=5 --bwlimit=200 "$rsync_file_to_copy" "$( mktemp -d )/" &
script2_pid=$!
wait $script2_pid
echo "Finished script2.sh"
./script3.sh
script3_pid=$!
wait $script3_pid
echo "Finished script3.sh"
script3.sh:
#!/usr/bin/env bash
function cleanup_script3()
{
echo "SIGINT in script3.sh"
}
trap cleanup_script3 SIGINT
delay_counter=0
while [ 8 -gt $delay_counter ]; do
echo "script3.sh: $( date "+%H_%M_%S" )"
let 'delay_counter++'
sleep 1
done
The output for execution of the above scripts is as follows:
The output for when CTRL+C during rsync:
$ ./script0.sh
1+0 records in
1+0 records out
1048576 bytes (1.0 MB, 1.0 MiB) copied, 0.00604244 s, 174 MB/s
^CSIGINT in script0.sh
rsync error: received SIGINT, SIGTERM, or SIGHUP (code 20) at rsync.c(703) [sender=3.2.3]
rsync error: received SIGINT, SIGTERM, or SIGHUP (code 20) at io.c(503) [generator=3.2.3]
Finished script2.sh
script3.sh: 23_57_24
script3.sh: 23_57_25
script3.sh: 23_57_26
script3.sh: 23_57_27
script3.sh: 23_57_28
script3.sh: 23_57_29
script3.sh: 23_57_30
script3.sh: 23_57_31
Finished script3.sh
The output for when CTRL+C during script3.sh:
$ ./script0.sh
1+0 records in
1+0 records out
1048576 bytes (1.0 MB, 1.0 MiB) copied, 0.00585018 s, 179 MB/s
Finished script2.sh
script3.sh: 23_57_46
script3.sh: 23_57_47
^CSIGINT in script0.sh
script3.sh: 23_57_48
script3.sh: 23_57_49
script3.sh: 23_57_50
script3.sh: 23_57_51
script3.sh: 23_57_52
script3.sh: 23_57_53
Finished script3.sh
script1
doing anything else? because otherwise thersync ... & wait;
could be replaced with the simplerrsync ...;
script0.sh
: If I write./script1.sh
instead of./script1.sh &
then script1.sh is able to detect SIGINT such that its handler is executed. Therefore, I conclude that the SIGINT handling was passed to it because it is part of the "foreground" execution.exec
, like so:exec > >(tee "${script_log_filepath}") 2>&1
. This caused the SIGINT to be ignored for script1.sh, so instead of the handler, the script would just exit. That can be solved with pipes (mkfifo
) instead of process substitution.