使用KeyboardInterrupt异常捕获SIGINT在终端中工作,而不是在脚本中

Vel*_*lda 13 python bash shell sigint

我试图SIGINT在Python 2.7程序中捕获(或键盘中断).这就是我的Python测试脚本的test外观:

#!/usr/bin/python

import time

try:
    time.sleep(100)
except KeyboardInterrupt:
    pass
except:
    print "error"
Run Code Online (Sandbox Code Playgroud)

接下来我有一个shell脚本test.sh:

./test & pid=$!
sleep 1
kill -s 2 $pid
Run Code Online (Sandbox Code Playgroud)

当我使用bash或sh或其他东西运行脚本时,bash test.shPython进程test保持运行并且无法使用SIGINT.而当我复制test.sh命令并将其粘贴到(bash)终端时,Python进程test将关闭.

我不知道发生了什么,我想了解.那么,差异在哪里,为什么?

这不是关于如何捕获SIGINTPython!根据文档 - 这是应该工作的方式:

Python默认安装少量信号处理程序:SIGPIPE ...和SIGINT转换为KeyboardInterrupt异常

这的确是追赶KeyboardInterruptSIGINT被发送kill,如果该程序是由外壳直接启动,但是当程序从后台运行bash脚本开始,似乎KeyboardInterrupt永远不会提高.

mat*_*ata 21

有一种情况是在启动时安装默认的sigint处理程序,即在程序启动时信号掩码包含SIG_IGNSIGINT情况.可以在此处找到负责此操作的代码.

被忽略信号的信号掩码从父进程继承,而处理信号被重置为SIG_DFL.因此,如果SIGINT被忽略,if (Handlers[SIGINT].func == DefaultHandler)源中的条件将不会触发并且未安装默认处理程序,在这种情况下,python不会覆盖父进程所做的设置.

因此,让我们尝试在不同的情况下显示使用过的信号处理程序:

# invocation from interactive shell
$ python -c "import signal; print(signal.getsignal(signal.SIGINT))"
<built-in function default_int_handler>

# background job in interactive shell
$ python -c "import signal; print(signal.getsignal(signal.SIGINT))" &
<built-in function default_int_handler>

# invocation in non interactive shell
$ sh -c 'python -c "import signal; print(signal.getsignal(signal.SIGINT))"'
<built-in function default_int_handler>

# background job in non-interactive shell
$ sh -c 'python -c "import signal; print(signal.getsignal(signal.SIGINT))" &'
1
Run Code Online (Sandbox Code Playgroud)

所以在最后一个例子中,SIGINT设置为1(SIG_IGN).这与在shell脚本中启动后台作业时相同,因为默认情况下这些是非交互式的(除非您-i在shebang中使用该选项).

所以这是由shell在非交互式shell会话中启动后台作业时忽略信号引起的,而不是直接由python引起的.至少bashdash以这种方式表现,我没有尝试过其他的贝壳.

有两种方法可以解决这种情况:


编辑:bash手册中记录了此行为:

SIGNALS
...
当作业控制无效时,除了这些继承的处理程序之外,异步命令还会忽略SIGINT和SIGQUIT.

这适用于非交互式shell,因为它们默认禁用了作业控制,并且实际上是在POSIX:Shell命令语言中指定的