How to catch SIGINT within a Bash subshell - linux

If I run a command, such as grep, at the command line and hit ^C, the command is properly killed (with SIGINT I think). And if I run the grep in background and then run a kill SIGINT on its PID, it similarly gets terminated. But if I'm inside a script and run grep in background from the script, get its PID and then use 'kill -s SIGINT $PID', grep does not get killed. Why? If I use SIGTERM, instead of SIGINT, the kill does work.
#!/bin/bash
grep -rqa shazam /usr &
PID=$!
kill -s SIGINT $PID
Even if I put the grep in a subprocess, preceded by a SIGINT handler (in the subprocess), and hit the subprocess with SIGINT, the handler is not invoked.
#!/bin/bash
( trap 'echo "caught signal"' SIGINT; grep -rqa shazam /usr ) &
PID=$!
kill -s SIGINT $PID
The trap handler is invoked if I use SIGTERM, instead of SIGINT, but does not interrupt grep. If I add '/bin/kill -s SIGTERM 0' to the trap handler, there is an indication that the grep process gets terminated, but grep has already completed its work by then. I realize that Bash may have different default behaviors for the different signals, but I don't understand why my call to kill SIGINT is different than a ^C, why the trap call works for SIGTERM, but not for SIGINT, nor why SIGTERM isn't handled by the subprocess immediately.

Well, with further digging, I figured out 2 of my 3 questions. When I backgrounded grep within the script, the shell told it to ignore SIGINT. And Bash says it will wait to handle the signal until the subcommand is complete in some situations (which I don't fully follow at the moment), but the signal is handled immediately if hit the grep process directly with pkill.

"Actually bash will disable SIGINT (and SIGQUIT) on background processes and they can't be enabled" Background process and signals How SIGINT works
"Further background jobs are not supposed to be tied to the shell that started them. If you exit a shell, they will continue running. As such they shouldn't be interrupted by SIGINT, not by default. When job control is enabled, that is fulfilled automatically, since background jobs are running in separate process groups. When job control is disabled (generally in non-interactive shells), bash makes the asynchronous commands ignore SIGINT." Independent Program
Reason why SIGTERM works

Related

`trap`-ing Linux Signals - SIGSTOP?

The Docker docs note the following (w/ code) on how to run clean up upon a container's shutdown:
Lastly, if you need to do some extra cleanup ... on shutdown, ..., you
may need to ensure that the ENTRYPOINT script receives the Unix
signals, passes them on, and then does some more work
#!/bin/sh
trap "echo TRAPed signal" HUP INT QUIT KILL TERM
/usr/sbin/apachectl start
I think that this trap will catch a KILL signal. However, I read in this post:
All signals except for SIGKILL and SIGSTOP can be intercepted by the process.
However, another post states:
There is one signal that you cannot trap: SIGKILL or signal 9.
Which is it?
You cannot catch SIGSTOP. The page you referenced was mistaken.
Yes, the above script tries to catch the KILL signal. It fails. You can verify this yourself quite easily with the following script:
#!/bin/sh
echo "Running shell in pid $$"
trap "echo TRAPed signal" HUP INT QUIT KILL TERM STOP
sleep 20
Try sending it the KILL and STOP signals, you will see that the process dies and halts, respectively, without the message being printed. If you try any other caught signal, you will see those are handled as expected.

How to launch a shell command on an already opened specific terminal?

I have a script ./runStart.sh that creates a new xterm and in this xterm launch the script run.log command.
Then, to finish the process I launch a script ./runStop.sh that kills the xterm and do other stuff behind.
The problem I have is that as I kill the process while running, there is no exit call, so the script command does not save the data in the actual file.
Do you know how I could launch a command in the second script ./runStop.sh that makes this exit call instead of the kill <pid> command ?
You can use the bash builtin trap to catch a signal (except KILL and STOP) and do the required cleanup.
For example, here i am catching the pseudo signal EXIT that would cover any signal that would make the shell to exit:
xterm -e '/bin/bash' -c 'trap "echo ok >/tmp/foobar" EXIT; sleep 5'
sleep 5 is my main task and echo ok >/tmp/foobar is cleanup task upon exiting.
Alternately if are sure about the signal to be sent/received e.g. HUP, INT, you can trap those directly.

How to kill a process by its pid in linux

I'm new in linux and I'm building a program that receives the name of a process, gets its PID (i have no problem with that part) and then pass the PID to the kill command but its not working. It goes something like this:
read -p "Process to kill: " proceso
proid= pidof $proceso
echo "$proid"
kill $proid
Can someone tell me why it isn't killing it ? I know that there are some other ways to do it, even with the PID, but none of them seems to work for me. I believe it's some kind of problem with the Bash language (which I just started learning).
Instead of this:
proid= pidof $proceso
You probably meant this:
proid=$(pidof $proceso)
Even so,
the program might not get killed.
By default, kill PID sends the TERM signal to the specified process,
giving it a chance to shut down in an orderly manner,
for example clean up resources it's using.
The strongest signal to send a process to kill without graceful cleanup is KILL, using kill -KILL PID or kill -9 PID.
I believe it's some kind of problem with the bash language (which I just started learning).
The original line you posted, proid= pidof $proceso should raise an error,
and Bash would print an error message about it.
Debugging problems starts by reading and understanding the error messages the software is trying to tell you.
kill expects you to tell it **how to kill*, so there must be 64 different ways to kill your process :) They have names and numbers. The most lethal is -9. Some interesting ones include:
SIGKILL - The SIGKILL (also -9) signal forces the process to stop executing immediately. The program cannot ignore this signal. This process does not get to clean-up either.
SIGHUP - The SIGHUP signal disconnects a process from the parent process. This an also be used to restart processes. For example, "killall -SIGUP compiz" will restart Compiz. This is useful for daemons with memory leaks.
SIGINT - This signal is the same as pressing ctrl-c. On some systems, "delete" + "break" sends the same signal to the process. The process is interrupted and stopped. However, the process can ignore this signal.
SIGQUIT - This is like SIGINT with the ability to make the process produce a core dump.
use the following command to display the port and PID of the process:
sudo netstat -plten
AND THEN
kill -9 PID
Here is an example to kill a process running on port 8283 and has PID=25334
You have to send the SIGKILL flag with the kill statement.
kill -9 [pid]
If you don't the operating system will choose to kill the process at its convenience, SIGKILL (-9) will tell the os to kill the process NOW without ignoring the command until later.
Try this
kill -9
It will kill any process with PID given in brackets
Try "kill -9 $proid" or "kill -SIGKILL $proid" commands. If you want more information, click.
Based on what you have there, it looks like you aren't getting the actual PID in your proid variable. If you want to capture the output of pidof, you will need to enclose that command in backtics for the old form of command substitution ...
proid=`pidof $proceso`
... or like so for the new form of command substitution.
proid=$(pidof $proceso)
I had a similar problem, only wanting to run monitor (Video surveillance) for several hours a day.
Wrote two sh scripts;
cat startmotion.sh
#!/bin/sh
motion -c /home/username/.config/motion/motion.conf
And the second;
cat killmotion.sh
#!/bin/sh
OA=$(cat /var/run/motion/motion.pid)
kill -9 $OA
These were called from crontab at the scheduled time
ctontab -e
0 15 * * * /home/username/startmotion.sh
0 17 * * * /home/username/killmotion.sh
Very simple, but that's all I needed.

bash: kill $$ not work?

The command kill $$ should kill current bash, but it seems that it doesn't work:
$ ps -p $$
PID TTY TIME CMD
18179 pts/4 00:00:00 bash
$ kill $$
$ ps -p $$
PID TTY TIME CMD
18179 pts/4 00:00:00 bash
Why?
I'm not sure why one would like to kill the current shell. Nevertheless...
kill PID would send SIGTERM when no signal is specified. bash ignores SIGTERM and SIGQUIT in the absence of any traps.
You'll achieve the desired effect if you were to say
kill -9 $$
or
kill -SIGKILL $$
Quoting from the manual:
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). When Bash receives a SIGINT, it breaks out of any
executing loops. In all cases, Bash ignores SIGQUIT.
When you send a process any signal using kill command, the process can choose to handle the signal as per its wish. (There are a few signals, which cannot be handled.)
When you use kill $$, you are actually passing it signal=15 (SIGTERM). It can be handled by a process.
You can google for linux signal example to know HOW it is implemented.
& To answer YOUR question:
If you pass an un-handlable signal, like SIGKILL(9) or SIGSTOP(19), it will respectively kill/stop a running bash process.

Killing a terminal-attached process that doesn't respond to SIGINT, SIGQUIT

Sometimes both Ctr-C (SIGINT) and Ctrl-\ (SIGQUIT) are too weak. Is there a way to do an more aggressive kill (e.g. kill -9) on the currently-attached process using a quick keyboard shortcut?
If you are a zsh user, you can send SIGTERM with this in .zshrc
function terminate-current-job() { kill -s TERM %+ ; }
zle -N terminate-current-job terminate-current-job
bindkey "^T" terminate-current-job
That binds CTRLT to the previously defined widget/function.
If you are having problems with a specific command not responding to CTRL-C (because it ignores SIGINT, or because it asked the terminal driver to no longer recognise it as an interrupt character) , you can try wrapping it in rlwrap:
rlwrap -a -I <command>
rlwrap will catch the SIGINT sent by the terminal driver when you press CTRL-C and send a SIGTERM to <command> instead.
Of course, <command> may catch, or even ignore SIGTERM as well, but many commands that ignore SIGINT will respond to SIGTERM - while still being able to clean up before they terminate, in contrast to what happens when you use SIGKILL (kill -9)
Like proposed in the other answers, you can try to kill the process by catching some other signal. This can be also done with the linux bash built in trap command
that is used to execute a command when the shell receives any signal
To KILL your executable if SIGINT (CTRL-C) is captured, you need to start it like this:
yourexecutable & pid=$! ; trap 'echo KILL ; kill -9 $pid' INT ; echo WAIT $pid ; wait $pid ; echo DONE
Note that the echos are just for debugging purposes, they can simply be removed if you don't need them.

Resources