python SIGINT不终止调用外壳

当从Linux shell(在bash和ksh中观察到同样的行为)运行Python并通过Ctl-C按键生成SIGINT时,我发现了我无法理解的行为,并且这让我非常沮丧。python SIGINT不终止调用外壳

当我按下Ctl-C时,Python进程正常终止,但shell继续执行下一个命令。

$ python -c "import time; time.sleep(100)"; echo END 

^CTraceback (most recent call last):

File "<string>", line 1, in <module>

KeyboardInterrupt

END

相比之下,我的预期,并希望,该外壳处理信号以这样的方式,执行不继续下一个命令就行了,因为我看到的时候我所说的睡眠功能从bash子shell而不是从Python。

$ bash -c "sleep 100"; echo END 

^C

Python 2和3被安装在我的系统,并同时产生上述捕获运行Python 2中,两个行为相同的方式。

$ python --version 

Python 2.7.14

$ python3 --version

Python 3.6.3

我最好的解释是,当我按CTL-C,而Python的进程正在运行时,信号以某种方式直接进入Python的过程,而通常它是由主叫壳处理,然后传播到子。但是,我不知道Python为什么或者如何导致这种差异。

上面的例子是简单的测试,但在现实世界的使用中也观察到了这种行为。安装自定义信号处理程序不能解决问题。

回答:

经过相当多的挖掘后,我发现堆栈溢出几个松散相关的问题,最终导致我到article describing the proper handling of SIGINT。 (最相关的部分是如何成为一个合适的程序。)

从这些信息,我能够解决这个问题。没有它,我就永远不会接近。

的解决方案是最好用不能由一个键盘中断终止bash脚本开始说明,但是这确实隐藏Python的KeyboardInterrupt异常丑陋的堆栈跟踪:

#!/usr/bin/env bash                

echo "Press Ctrl-C to stop... No sorry it won't work."

while true

do

python -c '

import time, signal

signal.signal(signal.SIGINT, signal.SIG_IGN)

time.sleep(100)

'

done

的变化,使外脚本处理中断是:

echo "Press Ctrl-C to stop..." 

while true

do

python -c '

import time, signal, os

signal.signal(signal.SIGINT, signal.SIG_DFL)

time.sleep(100)

'

done

但是,此解决方案使得不可能使用自定义处理程序(例如,执行清理)。如果要求这样做,则需要更复杂的方法:

#!/usr/bin/env bash 

echo "Press [CTRL+C] to stop ..."

while true

do

python -c '

import time, sys, signal, os

def handle_int(signum, frame):

# Cleanup code here

signal.signal(signum, signal.SIG_DFL)

os.kill(os.getpid(), signum)

signal.signal(signal.SIGINT, handle_int)

time.sleep(100)

'

done

的原因似乎是,除非内部过程终止通过执行由系统提供的默认SIGINT处理程序,父bash进程没有意识到该孩子因键盘中断而终止,并且本身不终止。

我还没有完全理解所有的辅助问题,比如父进程是不是从系统接收到SIGINT,还是正在接收信号,但忽略它。我也不知道默认处理程序是做什么的,或者父母如何检测它被调用。如果我能够学到更多,我会提供更新。

我必须提出一个问题:Python的当前行为是否应该被认为是Python中的一个设计缺陷。多年来,在从shell脚本中调用Python的过程中,我已经看到了这个问题的各种表现,但直到现在还没有调查过。但是,我还没有通过网络搜索找到一篇文章。如果问题确实代表了一个缺陷,那么让我感到惊讶的是,并不是很多开发人员受到影响。

回答:

任何获取CTRL + C的程序的行为都取决于该程序。通常行为是退出,但一些程序可能会放弃一些内部程序而不是停止整个程序。这甚至有可能(尽管可能被认为是不礼貌的做法)让程序完全忽略击键。

程序的行为是由它设置的信号处理程序定义的。 C库提供了默认的信号处理程序(它们在SIGTERM和SIGINT中执行退出操作),但是程序可以提供自己的处理程序来运行。并非所有信号都允许任意响应。例如,SIGSEGV(seg-fault)需要程序退出,尽管它可以配置其信号处理程序来进行核心转储。 SIGKILL根本无法处理(操作系统内核负责它)。

要在Python中定制信号处理程序,您需要使用标准库中的the signal module。您可以拨打signal.signal为系统C库定义的任何信号设置您自己的信号处理函数。键入CTRL + C将在任何基于UNIX的系统上发送SIGINT,所以这可能是您想要处理的内容,如果您想要自己的行为。

尝试是这样的:

import signal 

import sys

import time

def interrupt_handler(sig, frame):

sys.exit(1)

signal.signal(signal.SIGINT, interrupt_handler)

time.sleep(100)

如果您运行此脚本,用CTRL + C中断它,它应该默默退出,就像你的bash脚本一样。

以上是 python SIGINT不终止调用外壳 的全部内容, 来源链接: utcz.com/qa/260835.html

回到顶部