【问题标题】:Is there a way to never exit on KeyboardInterrupt in python?有没有办法永远不会在 python 中的 KeyboardInterrupt 上退出?
【发布时间】:2020-03-19 01:39:02
【问题描述】:

我正在 python 中创建一种交互式命令行。我有这样的事情:

def complete_menu():
    while True:
        cmd = input('cmd> ')
        if cmd == "help":
            print('help')
        elif cmd == "?":
            print('?')

当用户按下 CTRL-C,而不是退出程序时,我试图让它打印“请键入 exit 以退出”并返回到 while True。我现在有这样的事情:

if __name__ == "__main__":
    try:
       main()
    except KeyboardInterrupt:
        print('Please use exit to exit')
        complete_menu()

虽然这可行,但存在许多问题。首先,当第一次按下 CTRL-C 时,它会打印出文本并完美运行。但是,当用户第二次按下 CTRL-C 时,它会像其他任何程序一样在按下 CTRL-C 后出现一堆乱七八糟的文本。这可以解决吗?

【问题讨论】:

    标签: python python-3.x keyboardinterrupt


    【解决方案1】:

    更好的方法是注册一个信号处理程序:

    import signal
    
    def handler(signum, frame):
        print("Please use exit to exit")
        # or: just call sys.exit("goodbye")
    
    ...
    
    def main():
        signal.signal(signal.SIGINT, handler)  # prevent "crashing" with ctrl+C
        ...
    
    
    if __name__ == "__main__":
        main()
    

    现在,当您的代码中收到 Ctrl+C 时,将执行函数 handler,而不是引发 KeyboardInterrupt 异常。这是一个基本示例,在处理程序中自定义代码以执行您想要的操作。

    注意:我的建议是实际上让用户使用 Ctrl+C 退出,即执行您可能需要执行的任何清理代码运行,然后在此处调用sys.exit。需要更强信号才能杀死的程序很烦人。

    【讨论】:

    • while True 的幼稚替代方案更清洁的解决方案,并提供处理其他低级内容的灵活性,例如内存不足收割者试图杀死应用程序 (SIGKILL) 或使进程运行休眠(SIGTSTP、SIGSTOP、SIGTTIN、SIGTTOU)或唤醒它(SIGCONT)。
    • “我的建议是让用户使用 Ctrl+C 退出......需要更强信号才能杀死的程序很烦人。” -- 我明白你的意思,但这取决于程序。如果OP的程序调用其他程序或者保留变量,按Ctrl+C退出会很烦人,最好按Ctrl+D退出。
    • 是的,这就是为什么它应该对 SIGINT 进行清理。如果一个程序忽略了 Ctrl+C,那么可以肯定的是 Ctrl+\ (SIGQUIT) 可能很快就会出现。正确处理 Ctrl+C 并不妨碍您也支持交互式程序的 Ctrl+D (EOF) 用法.
    【解决方案2】:

    你快到了。这应该可以解决问题:

    if __name__ == "__main__":
        while True:
            try:
                main()
            except KeyboardInterrupt:
                print('Please use exit to exit')
                complete_menu()
    

    【讨论】:

    • 但是如何退出外部while 循环?您不应该将try-except 块放在内部while 循环中吗?
    • 这仅在我使用try 将代码包装在'while True' 中然后编写except KeyboardInterrupt 时才有效。它只在那里工作,而不是在if __name__ == "__main"__
    • @wjandrea 是的,但问题是,它甚至没有解决问题,答案只是隐藏无限“我们到了吗?”问题。如果您作为用户只需按住Ctrl + C 足够长的时间或结合Ctrl + S 暂停终端/进程然后尝试Ctrl + C,这几乎总是在普通终端中工作,则可以忽略这一点。 (如果没有为docker run 提供-i|--interactive,则不适用于Docker。)
    猜你喜欢
    • 2016-06-06
    • 2022-01-02
    • 2018-06-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-23
    • 2014-08-04
    • 2012-09-14
    相关资源
    最近更新 更多