Python 无法在命令提示符中捕获键盘中断两次?

Python 无法在命令提示符中捕获键盘中断两次?,python,windows,exception-handling,cmd,Python,Windows,Exception Handling,Cmd,今天,当我注意到一些奇怪的事情时,我不得不检查我的脚本如何在Windows命令提示符[1]上运行。我正在研究类似的东西,但这足以说明问题所在。这是密码 def bing(): try: raw_input() except KeyboardInterrupt: print 'This is what actually happened here!' try: # pardon me for those we

今天,当我注意到一些奇怪的事情时,我不得不检查我的脚本如何在Windows命令提示符[1]上运行。我正在研究类似的东西,但这足以说明问题所在。这是密码

def bing():
    try:
        raw_input()
    except KeyboardInterrupt:
        print 'This is what actually happened here!'

try:                     # pardon me for those weird strings
    bing()               # as it's consistent with everything in the chat room (see below)
    print 'Yoo hoo...'
except KeyboardInterrupt:
    print 'Nothing happens here too!'
情况是这样的。当脚本运行时,它等待输入,并且用户应该按Ctrl+C来引发
键盘中断
,该中断将(应该)被
捕获,除了
bing()中的
块。所以,这应该是实际输出。当我在我的Ubuntu终端上运行它并且空闲时(在Windows和Ubuntu上),就会发生这种情况

但是,这并不像Windows命令提示符上预期的那样。我宁愿得到一个奇怪的输出

This is what actually happened here! Nothing happens here too!
它看起来像一个
键盘中断
在整个程序中传播并最终终止它

我尽了我所能。首先,我使用一个
signal.signal
来处理
SIGINT
(不起作用),然后我使用handling函数来引发一个
异常
,我稍后会捕获它(也不起作用),然后事情变得比以前更复杂。所以,我回到了我的好老
尝试。。。捕捉
。然后,我去了蟒蛇医生的房间

当我们按下Ctrl+C时,
EOFError
被提升。然后,当按下Ctrl+Z并回车时,
EOFError
被提升

这是很有帮助的,这推动了整个过程。很快,一切都变得混乱起来!有些结果是好的,有些是出乎意料的,还有一些

结论是停止使用Windows,让我的朋友使用终端(我同意)。不过,我可以通过捕捉
eoferor
键盘中断来解决问题。虽然每次按Ctrl+Z并输入都感觉很懒,但这对我来说不是什么大问题。但是,这对我来说是一种困扰

在进一步的研究中,我还注意到当我按下Ctrl+C时,CMD上没有出现
键盘中断

底部什么都没有。那么,这里到底发生了什么?为什么
键盘中断
会传播?有没有办法使输出与终端一致


[1] :我一直在终端上工作,但今天我需要确保我的脚本在所有平台上都能工作(特别是因为我的大多数朋友都不是程序员,只是坚持使用Windows)。

链接的问题以某种方式解释了这一点:

键盘中断是异步引发的,因此不会立即终止应用程序。相反,Ctrl+C是在某种事件循环中处理的,需要一段时间才能到达。不幸的是,在这种情况下,这意味着您无法可靠地捕获
键盘中断。但是我们可以做一些事情来达到目的

正如我昨天解释的,停止
raw\u输入调用的异常不是
KeyboardInterrupt
,而是
eoferor
。您可以通过如下方式更改
bing
功能来轻松验证这一点:

def bing():
    try:
        raw_input()
    except Exception as e:
        print(type(e))
您将看到打印的异常类型是
eoferor
,而不是
KeyboardInterrupt
。您还将看到,
打印
甚至没有完全通过:没有新行。这显然是因为输出被中断了,中断是在print语句将异常类型写入stdout之后到达的。当您在打印中添加更多内容时,您也可以看到这一点:

def bing():
    try:
        raw_input()
    except EOFError as e:
        print 'Exception raised:', 'EOF Error'
注意,我在这里为print语句使用了两个独立的参数。执行此操作时,可以看到“引发异常”文本,但不会出现“EOF错误”。相反,来自外部调用的
除外将触发,并捕获键盘中断

不过,在Python3中,事情变得有点失控。以这个代码为例:

def bing():
    try:
        input()
    except Exception as e:
        print('Exception raised:', type(e))

try:
    bing()
    print('After bing')
except KeyboardInterrupt:
    print('Final KeyboardInterrupt')
这和我们之前做的差不多,只是针对Python3语法进行了修改。如果运行此操作,将获得以下输出:

Exception raised: <class 'EOFError'>
After bing
Final KeyboardInterrupt
现在,我们只需捕获EOFError并等待一段时间,让后面的异步进程稳定下来,然后决定是否中断执行。这始终允许我在外部try/catch中捕获
键盘中断
,并且除了在异常处理程序中执行的操作外,不会打印任何其他内容

你可能会担心等待一秒钟是很长的时间,但在我们的例子中,如果我们中断执行,那一秒钟永远不会持续很长时间。在
time.sleep
之后几毫秒,中断被捕获,我们进入了异常处理程序。因此,这一秒只是一个故障保护,它将等待足够长的时间,以确保异常一定会及时到达。在最坏的情况下,实际上没有中断,只有一个“正常”EOFError?然后,之前为用户输入而无限阻塞的程序将需要更长的一秒钟才能继续;这不应该是一个真正的问题(更不用说EOFError可能是非常罕见的)


所以我们有了我们的解决方案:抓住EOFError,再等一会儿。至少我希望这是一个在我自己的机器之外的其他机器上工作的解决方案^_^“昨晚之后,我对此不太确定,但至少我在所有终端和不同的Python版本上都获得了一致的体验。

这确实是解决问题的好方法。我已经设置好了捕捉
eoferor
的一切,现在我增加了100毫秒的等待时间(这对我来说已经足够了),这非常有效(多亏了你)。这允许我在中断终止主程序之前捕获中断。因此,双赢:)
PyOS_Readline
(第55-75行)Windows控制台控制事件到达新线程的事实。请为我尝试以下操作:
从ctypes导入*
kernel32=windell('kernel32',use\u last\u error=True)
kernel32.ReadFile(内核
Exception raised: <class 'EOFError'>
After bing
Final KeyboardInterrupt
import time
def bing():
    try:
        input() # or raw_input() for Python 2
    except EOFError:
        time.sleep(1)

try:
    bing()
    print('After bing')
except KeyboardInterrupt:
    print('Final KeyboardInterrupt')