Python:Catch Ctrl-C命令.提示"真的想退出(y/n)",如果没有,则恢复执行

Col*_*n M 34 python signals copy-paste sigint

我有一个可能有长时间执行的程序.在主模块中,我有以下内容:

import signal
def run_program()
   ...time consuming execution...

def Exit_gracefully(signal, frame):
    ... log exiting information ...
    ... close any open files ...
    sys.exit(0)

if __name__ == '__main__':
    signal.signal(signal.SIGINT, Exit_gracefully)
    run_program()
Run Code Online (Sandbox Code Playgroud)

这样可以正常工作,但是我希望能够在捕获SIGINT时暂停执行,提示用户是否真的想要退出,如果他们决定不想退出,则继续在run_program()中停止.

我能想到这样做的唯一方法是在一个单独的线程中运行程序,保持主线程等待它并准备捕获SIGINT.如果用户想要退出主线程可以做清理并杀死子线程.

有更简单的方法吗?

Ant*_*ala 56

python信号处理程序似乎不是真正的信号处理程序; 也就是说,它们发生在事实之后,正常流程中以及C处理程序已经返回之后.因此,您将尝试将退出逻辑放在信号处理程序中.由于信号处理程序在主线程中运行,它也会阻止执行.

像这样的东西似乎工作得很好.

import signal
import time
import sys

def run_program():
    while True:
        time.sleep(1)
        print("a")

def exit_gracefully(signum, frame):
    # restore the original signal handler as otherwise evil things will happen
    # in raw_input when CTRL+C is pressed, and our signal handler is not re-entrant
    signal.signal(signal.SIGINT, original_sigint)

    try:
        if raw_input("\nReally quit? (y/n)> ").lower().startswith('y'):
            sys.exit(1)

    except KeyboardInterrupt:
        print("Ok ok, quitting")
        sys.exit(1)

    # restore the exit gracefully handler here    
    signal.signal(signal.SIGINT, exit_gracefully)

if __name__ == '__main__':
    # store the original SIGINT handler
    original_sigint = signal.getsignal(signal.SIGINT)
    signal.signal(signal.SIGINT, exit_gracefully)
    run_program()
Run Code Online (Sandbox Code Playgroud)

代码恢复原始信号处理程序的持续时间raw_input; raw_input它本身是不可重复的,重新进入它将导致RuntimeError: can't re-enter readline被提升,time.sleep这是我们不想要的东西,因为它比它更难捕捉KeyboardInterrupt.相反,我们让2个连续的Ctrl-C加注KeyboardInterrupt.

  • 哇!这真的很酷.但是,我认为crtl-c离开提示符的能力​​应该在装饰器中,因为它使代码更不会神秘.提供这种方式作为答案是否合适(因为我无法编辑你回答添加这种替代方式)? (2认同)
  • 如果我们有多线程应用程序,该策略不起作用. (2认同)

小智 7

来自https://gist.github.com/rtfpessoa/e3b1fe0bbfcd8ac853bf

#!/usr/bin/env python

import signal
import sys

def signal_handler(signal, frame):
  # your code here
  sys.exit(0)

signal.signal(signal.SIGINT, signal_handler)
Run Code Online (Sandbox Code Playgroud)

再见!