使用 KeyboardInterrupt 异常捕获 SIGINT 在终端中有效,而不是在脚本中有效
Capturing SIGINT using KeyboardInterrupt exception works in terminal, not in script
我正在尝试在 Python 2.7 程序中捕获 SIGINT
(或键盘中断)。这是我的 Python 测试脚本 test
的样子:
#!/usr/bin/python
import time
try:
time.sleep(100)
except KeyboardInterrupt:
pass
except:
print "error"
接下来我有一个 shell 脚本 test.sh
:
./test & pid=$!
sleep 1
kill -s 2 $pid
当我 运行 使用 bash 或 sh 或其他内容 bash test.sh
的脚本时,Python 进程 test
保持 运行 ning 并且不能用 SIGINT
杀死。而当我复制 test.sh
命令并将其粘贴到 (bash) 终端时,Python 进程 test
关闭。
我不明白发生了什么,我想了解一下。那么,区别在哪里,为什么?
这不是关于 如何在 Python 中捕获 SIGINT
! 根据 docs – 这是方法,应该可行:
Python installs a small number of signal handlers by default: SIGPIPE ... and SIGINT is translated into a KeyboardInterrupt exception
如果直接从shell启动程序,kill
发送SIGINT
时确实是在捕获KeyboardInterrupt
,但是当程序从[=]启动时46=] 脚本 运行 在后台,似乎从未引发 KeyboardInterrupt
。
有一种情况是在启动时 未安装 默认的 sigint 处理程序,即信号掩码包含 SIG_IGN
for SIGINT
在程序启动时。可以找到负责此的代码 here.
忽略信号的信号掩码继承自父进程,而处理的信号重置为 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
因此在最后一个示例中,SIGINT
设置为 1 (SIG_IGN
)。这与在 shell 脚本中启动后台作业时相同,因为默认情况下它们是非交互式的(除非您在 shebang 中使用 -i
选项)。
所以这是由 shell 在非交互式 shell 会话中启动后台作业时忽略信号引起的,而不是由 python 直接引起的。至少 bash
和 dash
是这样的,我没试过其他 shells.
有两种方案可以处理这种情况:
手动安装默认信号处理程序:
import signal
signal.signal(signal.SIGINT, signal.default_int_handler)
将 -i
选项添加到 shell 脚本的 shebang,例如:
#!/bin/sh -i
编辑:此行为记录在 bash 手册中:
SIGNALS
...
When job control is not in effect, asynchronous commands ignore SIGINT and SIGQUIT in addition to these inherited handlers.
适用于非交互式 shells,因为它们默认禁用作业控制,实际上在 POSIX 中指定:Shell Command Language
我正在尝试在 Python 2.7 程序中捕获 SIGINT
(或键盘中断)。这是我的 Python 测试脚本 test
的样子:
#!/usr/bin/python
import time
try:
time.sleep(100)
except KeyboardInterrupt:
pass
except:
print "error"
接下来我有一个 shell 脚本 test.sh
:
./test & pid=$!
sleep 1
kill -s 2 $pid
当我 运行 使用 bash 或 sh 或其他内容 bash test.sh
的脚本时,Python 进程 test
保持 运行 ning 并且不能用 SIGINT
杀死。而当我复制 test.sh
命令并将其粘贴到 (bash) 终端时,Python 进程 test
关闭。
我不明白发生了什么,我想了解一下。那么,区别在哪里,为什么?
这不是关于 如何在 Python 中捕获 SIGINT
! 根据 docs – 这是方法,应该可行:
Python installs a small number of signal handlers by default: SIGPIPE ... and SIGINT is translated into a KeyboardInterrupt exception
如果直接从shell启动程序,kill
发送SIGINT
时确实是在捕获KeyboardInterrupt
,但是当程序从[=]启动时46=] 脚本 运行 在后台,似乎从未引发 KeyboardInterrupt
。
有一种情况是在启动时 未安装 默认的 sigint 处理程序,即信号掩码包含 SIG_IGN
for SIGINT
在程序启动时。可以找到负责此的代码 here.
忽略信号的信号掩码继承自父进程,而处理的信号重置为 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
因此在最后一个示例中,SIGINT
设置为 1 (SIG_IGN
)。这与在 shell 脚本中启动后台作业时相同,因为默认情况下它们是非交互式的(除非您在 shebang 中使用 -i
选项)。
所以这是由 shell 在非交互式 shell 会话中启动后台作业时忽略信号引起的,而不是由 python 直接引起的。至少 bash
和 dash
是这样的,我没试过其他 shells.
有两种方案可以处理这种情况:
手动安装默认信号处理程序:
import signal signal.signal(signal.SIGINT, signal.default_int_handler)
将
-i
选项添加到 shell 脚本的 shebang,例如:#!/bin/sh -i
编辑:此行为记录在 bash 手册中:
SIGNALS
...
When job control is not in effect, asynchronous commands ignore SIGINT and SIGQUIT in addition to these inherited handlers.
适用于非交互式 shells,因为它们默认禁用作业控制,实际上在 POSIX 中指定:Shell Command Language