以下代码作为Saving work after a SIGINT
的参考class Main(object):
def do_stuff(self):
...
def save_work(self):
...
def __init__(self):
try:
self.do_stuff()
except KeyboardInterrupt:
pass # Or print helpful info
self.save_work()
如果没有子进程,这种方法非常好。
但是,只要在save_work()中调用子进程,子进程就不会在收到SIGINT信号时执行。
所以,执行
cmd = r"hadoop fs -put '{}' '{}'".format(
src, dest)
process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
无效。
该解决方法是什么?
答案 0 :(得分:0)
如上所述对您的问题/问题的最短答案:将subprocess.Popen
替换为subprocess.call
或其中一个(例如检查)变体。或者添加process.communicate()
。
发生了什么事,为什么它看起来像#34;不起作用"。 Popen
打开了用于通信的管道,并根据需要分叉了一个进程。但是,管道在父进程端(你从中调用它)没有任何东西读取它实际上可以导致子进程(写入stdout / stderr)非常快速地进入阻塞I / O.与此同时,您的父级继续运行,因为没有任何东西告诉它等待其子级,并最终终止子进程收到SIGPIPE
(默认操作将终止)。
我们有test.sh
:
#!/bin/bash
handle_sigpipe() {
echo "GOT SIGPIPE" >> OUT
exit 0
}
trap handle_sigpipe SIGPIPE
echo line1 > OUT
echo line2
echo line3 >> OUT
和一个小python脚本调用它类似于你的问题:
import time
import subprocess
try:
time.sleep(20)
except KeyboardInterrupt:
cmd = "./test.sh"
process = subprocess.Popen(cmd, shell=True,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
现在我们打电话并打断它:
$ python3 test.py
^C
$ cat OUT
line1
GOT SIGPIPE
我们已经写了line1
,但是当脚本尝试编写line2
时,它最终会等待有人从接收和管道中读取(至少在通过shell调用时) ,这些stdout写入是行缓冲的)。同时,父母完成并关闭管道的末端。 test.sh
收到SIGPIPE
,处理程序将其写入文件,shell脚本终止。
如果您实际上通常希望在脚本退出时执行清理/保存工作(也是在中断时)。 atexit
是这样做的常用方法。如果您想要处理特定信号(例如SIGINT
),您还可以查看signal.signal
。
答案 1 :(得分:0)
上述问题是由重构代码解决的。级联异常阻止了信号。
最终的解决方案是。
def signal_handler(sign, frame):
logger.warn('app has been terminated manually with signal {} at frame {}'.format(sign, frame))
sys.exit(1)
def end_programm():
upload_log_file()
def main():
[...]
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
atexit.register(end_programm)
[...]