python SIGINT没有终止调用shell

时间:2017-12-18 04:30:06

标签: python linux

当从Linux shell运行Python(在bash和ksh中都观察到相同的行为),并使用Ctl-C keypress生成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

相比之下,我曾预料到并且希望shell处理信号的方式是执行不会继续执行下一行命令,就像我从bash子shell调用sleep函数时看到的那样而不是来自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

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

上面的例子是微不足道的测试,但在实际使用中也观察到了这种行为。安装自定义信号处理程序无法解决问题。

3 个答案:

答案 0 :(得分:0)

The behavior of any program that gets a CTRL+C is up to that program. Usually the behavior is to exit, but some programs might just abort some internal procedure instead of stopping the whole program. It's even possible (though it may be considered bad manners) for a program to ignore the keystroke completely.

The behavior of the program is defined by the signal handlers it has set up. The C library provides default signal handlers (which do things like exit on SIGTERM and SIGINT), but a program can provide its own handlers that will run instead. Not all signals allow arbitrary responses. For instance, SIGSEGV (a seg-fault) requires the program to exit, though it can configure its signal handlers to make a core dump or not. SIGKILL can't be handled at all (the OS kernel takes care of it).

To customize signal handlers in Python, you'll want to use the signal module from the standard library. You can call Select * from [Mytable] Where BPT = 'NH' and BC = 'ABH6G' union Select * from [Mytable] Where BPT = 'NH' and BC = '' And BPT+BC+ST not in (select BPT + '' + ST from [Mytable] Where BPT = 'NH' and BC = 'ABH6G') to set your own signal handler function for any of the signals defined by your system's C library. Typing CTRL+C is going to send SIGINT on any UNIX-based system, so that's probably what you'll want to handle if you want your own behavior.

Try something like this:

signal.signal

If you run this script and interrupt it with CTRL+C, it should exit silently, just like your bash script does.

答案 1 :(得分:0)

经过大量挖掘后,我发现了一些关于Stack Overflow的松散相关问题,最终导致我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处理程序而终止,否则父进程没有意识到子进程由于键盘中断而终止,并且本身不会终止。

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

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

答案 2 :(得分:0)

您可以在脚本文件的bash端显式处理它,如下所示:

Sub GetMotherboards()
    Dim ie                      As InternetExplorer
    Set ie = New InternetExplorer

    Dim doc                     As HTMLDocument
    Dim objText                 As DataObject
    Dim objArticleContents      As Object
    Dim objLinksCollection      As Object
    Dim objToClipBoard          As DataObject
    Dim r As Object
    Dim prodRating              As String
    Dim prodName                As String
    Dim lngNumberOfVideos As Long
    Dim strURL                  As String
    Dim strNewString As String, strStr As String, strTestChar As String
    Dim bFlag As Boolean

    strURL = "https://pcpartpicker.com/products/motherboard/" ' Range("J5").Value
    With ie
        .navigate strURL
        .Visible = True
        Do While .readyState <> 4: DoEvents: Loop
        Application.Wait Now + #12:00:02 AM#

        Set doc = ie.document
    End With
    bFlag = False
    With doc
        Set objArticleContents = .getElementsByClassName("subTitle__form")

        Stop
        Set ele = .getElementsByClassName("subTitle__form")(0)

        Set form = .getElementsByClassName("subTitle__form")(0).getElementsByClassName("form-label xs-inline")(1)

        Set inzputz = ele.getElementsByClassName("text-input")(0)
        Call .getElementsByClassName("text-input")(0).setAttribute("placeholder", "MSI B450 TOMAHAWK") '.setAttribute("part_category_search", "MSI B450 TOMAHAWK")
    End With

End Sub

或更积极地

if python -c "import time; time.sleep(100)"; then
  echo END
fi

$?是上一条命令的返回状态代码。状态代码为0表示退出正常,而其他所有内容均为错误。因此,如果前面的命令失败,我们将使用&&的短路特性来简洁地退出。

(有关更多信息,请参见https://unix.stackexchange.com/questions/186826/parent-script-continues-when-child-exits-with-non-zero-exit-code

注意:这会因为任何类型的python失败而退出bash脚本,而不仅仅是ctrl + c,例如IndexError,AssertionError等