Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to send a SIGINT to Python from a bash script?

Tags:

python

bash

I want to launch a background Python job from a bash script and then gracefully kill it with SIGINT. This works fine from the shell, but I can't seem to get it to work in a script.

loop.py:

#! /usr/bin/env python
if __name__ == "__main__":
    try:
        print 'starting loop'
        while True:
            pass
    except KeyboardInterrupt:
        print 'quitting loop'

From the shell I can interrupt it:

$ python loop.py &
[1] 15420
starting loop
$ kill -SIGINT 15420
quitting loop
[1]+  Done                    python loop.py

kill.sh:

#! /bin/bash
python loop.py &
PID=$!
echo "sending SIGINT to process $PID"
kill -SIGINT $PID

But from a script I can't:

$ ./kill.sh 
starting loop
sending SIGINT to process 15452
$ ps ax | grep loop.py | grep -v grep
15452 pts/3    R      0:08 python loop.py

And, if it's been launched from a script I can no longer kill it from the shell:

$ kill -SIGINT 15452
$ ps ax | grep loop.py | grep -v grep
15452 pts/3    R      0:34 python loop.py

I'm assuming I'm missing some fine point of bash job control.

like image 220
Ryan Avatar asked Jun 10 '09 07:06

Ryan


People also ask

How do you catch Sigint in bash?

The most common signal of bash is SIGINT (Signal Interrupt). When the user presses CTRL+C to interrupt any process from the terminal then this signal is sent to notify the system.

Does bash forward signals?

In this case the TERM signal is received by the shell process, but Bash will not forward that signal to the child process. This means that the shell process will stop, but the JVM will continue to run.

Can Python call a shell script?

If you need to execute a shell command with Python, there are two ways. You can either use the subprocess module or the RunShellCommand() function. The first option is easier to run one line of code and then exit, but it isn't as flexible when using arguments or producing text output.

Can you call a bash script from Python?

We can also execute an existing a bash script using Python subprocess module.


1 Answers

You're not registering a signal handler. Try the below. It seems to work fairly reliably. I think the rare exception is when it catches the signal before Python registers the script's handler. Note that KeyboardInterrupt is only supposed to be raised, "when the user hits the interrupt key". I think the fact that it works for a explicit (e.g. via kill) SIGINT at all is an accident of implementation.

import signal

def quit_gracefully(*args):
    print 'quitting loop'
    exit(0);

if __name__ == "__main__":
    signal.signal(signal.SIGINT, quit_gracefully)

    try:
        print 'starting loop'
        while True:
            pass
    except KeyboardInterrupt:
        quit_gracefully()
like image 59
Matthew Flaschen Avatar answered Oct 22 '22 21:10

Matthew Flaschen