1

I have a bash shell script which calls a Python script in an endless loop:

while true; do python testomat.py ; done  

The Python script testomat.py:

try:
    do_something()
except KeyboardInterrupt:
    print 'exit'
finally:
    raise SystemExit

The problem I'm having is that I cannot stop this by Ctrl-C. My only chance: Ctrl-z and then kill -TERM %1 assuming that I have no other jobs running. Would Python somehow have to propagate Ctrl-C to the parent or how would I be able to stop this?

I know I could run the endless loop inside Python, but I actually do more in both the bash shell script and the Python script which both must continue to exist.

Michael
  • 445
  • 4
  • 16

2 Answers2

2

Set an exit status in the Python script and catch it in the Bash script. I would use 130 since that's what coreutils like grep and find use.

except KeyboardInterrupt:
    sys.exit(130)
while true; do
    python testomat.py
    case $? in
    130) break ;;
    esac
done
wjandrea
  • 28,235
  • 9
  • 60
  • 81
  • Use `SystemExit(130)` in Python 2 as well; the old tuple-like syntax was retained only for backwards compatibility with very old versions of Python. (Actually, don't raise `SystemExit` explicitly at all; use `sys.exit(130)`.) – chepner Oct 06 '19 at 00:38
2

You'll notice that you generally can interrupt shell loops. For example, this loop will exit just fine when you hit Ctrl-C:

while true; do sleep 10; done

A parent process -- here the shell -- can tell whether a child exited normally, or whether it was killed by a signal. If it was killed by a signal, the shell will consider the signal unhandled and will stop the loop and/or script. If the child receives a signal but exits normally, the shell will consider it handled and continue.

When a process wants to do some cleanup, it necessarily handles the signal and therefore isn't killed. The canonical Unix behavior is therefore, when cleanup is done, to uninstall the signal handler and re-kill yourself:

import time
import os
import signal

try:
  time.sleep(10);
except KeyboardInterrupt:
  print('exit')
  signal.signal(signal.SIGINT, signal.SIG_DFL)
  os.kill(os.getpid(), signal.SIGINT)

Since the cause of death is being correctly relayed back to the shell, you will now be able to Ctrl-C out of your while true; do python yourfile.py; done loop.

that other guy
  • 116,971
  • 11
  • 170
  • 194