Backgound
I have read a post about handling of SIGINT
signal, but I still don't understand how to properly handle it in code which will be sourced and used by both the interactive and non-interactive shells.
I will give an simplified example of my script and ask questions for specific parts.
Example
I have a script with useful functions which is meant to be sourced and used anywhere.
/tmp/useful_functions.sh
#!/bin/bash
function example()
{
echo "Main script started"
# Make process run forever in the background.
( while sleep 1; do echo "Background script running"; done ) &
# Make subshell work for 10 seconds.
local output="$(sleep 10; echo "Subshell completed")"
echo "${output}"
# Kill the backgrounded process once the subshell has finished.
{ kill "${!}" && wait "${!}"; } &> /dev/null
echo "Main script completed"
}
Test 1
Lets use the example function in another script.
/tmp/test.sh
#!/bin/bash
source /tmp/useful_functions.sh
example
Now lets run /tmp/test.sh
and press Control-C
while subshell is working.
(subshell = $(sleep 10; echo "Subshell completed")
)
Results from Test 1
/tmp/test.sh
, example
, subshell
and background
processes were terminated.
Prompt was waiting for new input.
Questions
- In which order was
SIGINT
sent/handled/propagated between processes?
(Mentioned post claims thatSIGINT
should be sent to all foreground processes and that the most-inner one should first handle it.) - Why was the backgrounded process terminated?
Test 2
Lets use the example function directly in interactive Bash:
...$ source /tmp/useful_functions.sh
...$ example
and again press Control-C
while subshell is working.
(subshell = $(sleep 10; echo "Subshell completed")
)
Results from Test 2
example
and subshell
processes were terminated, but backgrounded
process stayed alive.
Prompt was waiting for new input, even though output from backgrounded
process was printed.
Questions
- In which order was
SIGINT
sent/handled/propagated between processes? - Why was the backgrounded process NOT terminated now?
Improved example
Even though I don't totaly understand the behavior from Test 1
, that behavior is desired one and I wanted to acomplish that with interactive Bash as well.
Idea was to create another subshell which wraps the backgrounded process and the existing subshell and hope that termination of that new procces will lead to termination of the backgrounded process and existing subshell.
/tmp/useful_functions.sh
#!/bin/bash
function example()
{
(
echo "Main script started"
# Make process run forever in the background.
( while sleep 1; do echo "Background script running"; done ) &
# Make subshell work for 10 seconds.
local output="$(sleep 10; echo "Subshell completed")"
echo "${output}"
# Kill the backgrounded process once the subshell has finished.
{ kill "${!}" && wait "${!}"; } &> /dev/null
echo "Main script completed"
)
}
Results from repeated Test 1
/tmp/test.sh
, example
, outer subshell
, inner subshell
and background
processes were terminated.
Prompt was waiting for new input.
Questions
- In which order was
SIGINT
sent/handled/propagated between processes? - Why was the backgrounded process terminated?
Results from repeated Test 2
example
, outer subshell
, inner subshell
and background
processes were terminated.
Prompt was waiting for new input.
Questions
- In which order was
SIGINT
sent/handled/propagated between processes? - Why was the backgrounded process terminated?
Problem
At this point I realised that I actually have to do some cleanup after the backgrounded process and that I need to trap SIGINT
somewhere to handle it.
Solution
Because I don't know how SIGINT
is handled and propagated generally in Bash, I made a few educated guesses and came up with solution which I think works properly, but can't be sure.
#!/bin/bash
function example()
{
(
echo "Main script started"
# Make process run forever in the background.
(
trap "echo Cleanup; trap - SIGINT; kill -s SIGINT ${$}" SIGINT
while sleep 1; do echo "Background script running"; done
) &
# Make subshell work for 10 seconds.
local output="$(sleep 10; echo "Subshell completed")"
echo "${output}"
# Kill the backgrounded process once the subshell has finished.
{ kill "${!}" && wait "${!}"; } &> /dev/null
echo "Main script completed"
)
}
Questions
- Does my trap properly handle/propagate
SIGINT
? - Is
${$}
the correct process?
(In mentioned post, it is said that the process should kill itslef, but their example uses${$}
instead of${BASHPID}
.)
nohup
or disowned then they'll ignore it. – slm Aug 06 '18 at 02:16