bash traps: letting command finish before ctrl+c is processed - bash

trap should be executed after the currently running command is finished, but in the following example upon pressing Ctrl+C it interrupts the current command (sleep) before the 10 seconds are over , prints the message "SIGINT received" and then immediately starts the next sleep:
#!/bin/bash
trap 'echo SIGINT received' INT
counter=0
while true
do
let counter=counter+1
echo start sleeping period $counter
sleep 10
done
Is there anything wrong with my use of trap? How can I achieve a behaviour that lets the current command finish after pressing Ctrl+C, without using a child process or subshell (this way is shown here)?

Ctrl+C interrupts the current foreground group, which in your case is both the script itself and sleep.
Since sleep dies on SIGINT, it exits immediately. This causes the message to be printed immediately.
If you want to be able to send a SIGINT to your process without it dying, you can ignore the signal before starting the process and hope that the process doesn't reset the signal action (most programs don't):
#!/bin/bash
trap 'echo SIGINT received' INT
counter=0
while true
do
let counter=counter+1
echo "start sleeping period $counter"
( trap '' INT; exec sleep 10; )
done

Related

How can I send a signal without the shell waiting for the currently running program to finish?

If I send a signal using kill, it seems to wait until the current program (in this example sleep 1000) finishes running. When I instead send SIGINT via pressing Ctrl+C in the shell, it receives the interrupt immediately however.
What I want, however, is for the interrupt to be received immediately after sending the signal via kill. Also, why does it behave like I would want it to when I press Ctrl+C?
#!/usr/bin/env sh
int_after_a_while() {
local pid=$1
sleep 2
echo "Attempting to kill $pid with SIGINT"
# Here I want to kill the process immediately, but it waits until sleep finishes
kill -s INT $pid
}
trap "echo Interrupt received!" INT
int_after_a_while $$ &
sleep 1000
I would appreciate any help on this issue. Thanks in advance!
As noted in the referenced answer https://unix.stackexchange.com/questions/282525/why-did-my-trap-not-trigger/282631#282631 the shell will normally wait for a utility to complete before running a trap. Some alternatives are:
Start the long running process in the background, then wait for it using the wait builtin. When a trapped signal is received during such a wait, the wait is interrupted and the trap is taken. Unfortunately, the exit status of wait does not distinguish between the child process exiting on a signal and a trap occurring. For example
sleep 1000 &
p=$!
wait "$p"
Send a signal to the whole process group via kill -s INT 0. The effect is much like if the user had pressed Ctrl+C, but may be more extreme than you want if your script is run from another script.
Use a shell such as zsh or FreeBSD sh that supports set -o trapsasync which allows running traps while waiting for a foreground job.

Handling signals in bash script when it started as a background process

Okay, I have a script like this:
trap 'echo "CTRL-C signal was caught!" ' SIGINT
for ((i=0; i<15; i++))
do
sleep 3
done
When I start my script in a usual way, it immediately reacts to CTRL-C command and echo "CTRL-C signal was caught!", even if there is a sleep 3 command. But when I run my script as a background process, it waits until sleep 3 command is finished, and then echo "CTRL-C signal was caught!"
I do not understand this. I think trap should wait until previous command is finished, and then it should echo something, like when it started as a background process.
Bash manual states:
Background processes (...) are immune to keyboard-generated signals.
If bash is waiting for a command to complete and receives a signal for which a trap has been set, the trap will not be executed until the command completes.
Consequently:
If your script runs in the foreground: when you press "Ctrl-C", a SIGINT is sent to the currently running process (i.e. the sleep command). The exit status of sleep tells Bash that it was interrupted by the SIGINT signal and bash calls your trap.
If your script runs in the background, then the backgrounded sleep does not receive the signal and the SIGINT trap is only executed once sleep has ended.

"kill -INT $pid" won't kill process, but ctrl+c will

I have a basic bash script that prints out its pid, then counts to 100:
echo $$
for i in {1..100}
do
echo $i
sleep 1
done
When I press ctrl+c, while this is running, the program will halt with an exit code of 130. I am trying to send a signal that will simulate a ctrl+c and cause the program to halt and exit with code 130.
When I type "kill -INT " from a different terminal window, nothing happens, though from what I understand, it should be the same as hitting ctrl+c. However, when I type "kill -9 ", I can successfully terminate the program, albeit with a different exit code. How do I stop my program with an interrupt signal, so it exits with the proper code?
Prepend process id with dash sign "-"
kill -SIGINT -<pid>
This will kill the process with exit code 130.
UPD: Why not to use SIGTERM(15) which is send by kill command by default (with no signal number or name)?

How can I run a program until the user presses ctrl+c in bash?

I am trying to write a bash script in which I require to run a binary and if I press Ctrl+c then along with script, binary which is running in background should also be stopped. I am trying this code but it does not help:
#!/bin/bash
cd /usr/local/path-to-binary
./testBinary
read input
if (( "$input" == "Ctrl+c")); then
ps -aef|grep Binary
pkill -9 Binary
There a several errors in your script. Check the comments below your question.
Here is an example how to launch a background process and kill it on Ctrl+c:
#!/bin/bash
# Register signal handler for SIGINT (Ctrl+c)
trap abort INT
function abort() {
echo "Sending SIGINT to background process ${pid}"
# Kill background process
kill "${pid}"
# Wait for it to finish after killing it
wait "${pid}"
# Exit the script
echo "Aborting"
exit 1
}
# Start long running process in background (&)
sleep 1000 &
# Obtain the pid of that process
pid=$!
# Wait for the background process to finish
wait
PS: Just run the script and press Ctrl+c. read is not required.

Trapping SIGINT in a backgrounded process

I am trying to understand some sample code describing signal handling in bash. In Example 32-7 at http://tldp.org/LDP/abs/html/debugging.html, the writer's comments state that he is capturing a SIGINT, yet the trap is for EXIT.
{
trap "exit" SIGUSR1
sleep $interval; sleep $interval
while true; do
...
done; } & # Start a progress bar as a background process.
pid=$!
trap "echo !; kill -USR1 $pid; wait $pid" EXIT # To handle ^C.
Why does a trap of EXIT send the correct signal (SIGUSR1) to the backgroud process on a SIGINT (Ctl-C)?
Any help is appreciated explaining why this works.
EXIT is a special handler in trap for bash, it's not a signal. There is no exit signal. This trap gets executed whenever the bash processes terminates. So, what this does is make sure that if the user kills the bash process, SIGUSR1 is sent to the background process, which also is trapped and then executes 'exit' on that process. That makes sure if you kill the session, the background process doesn't live on forever but also quits (which is probably what the comment is trying to explain).
edit: I misread this question in my original response
The EXIT pseudo-signal is raised both on normal exit and when the script is being interrupted.

Resources