子进程超时失败

时间:2016-04-30 07:42:53

标签: python subprocess

我想在子进程上使用超时

 from subprocess32 import check_output
 output = check_output("sleep 30", shell=True, timeout=1)

不幸的是,虽然这会引发超时错误,但它会在30秒后发生。似乎check_output不能中断shell命令。

我可以在Python端做些什么来阻止它? 我怀疑subprocess32无法终止超时过程。

2 个答案:

答案 0 :(得分:22)

check_output() with timeout is essentially

with Popen(*popenargs, stdout=PIPE, **kwargs) as process:
    try:
        output, unused_err = process.communicate(inputdata, timeout=timeout)
    except TimeoutExpired:
        process.kill()
        output, unused_err = process.communicate()
        raise TimeoutExpired(process.args, timeout, output=output)

有两个问题:

它会导致您观察到的行为:TimeoutExpired在一秒内发生,shell被终止,但check_output()仅在孙sleep进程退出后30秒内返回。

要解决问题,请终止整个进程树(属于同一组的所有子进程):

#!/usr/bin/env python3
import os
import signal
from subprocess import Popen, PIPE, TimeoutExpired
from time import monotonic as timer

start = timer()
with Popen('sleep 30', shell=True, stdout=PIPE, preexec_fn=os.setsid) as process:
    try:
        output = process.communicate(timeout=1)[0]
    except TimeoutExpired:
        os.killpg(process.pid, signal.SIGINT) # send signal to the process group
        output = process.communicate()[0]
print('Elapsed seconds: {:.2f}'.format(timer() - start))

输出

Elapsed seconds: 1.00

答案 1 :(得分:1)

针对Python 3.6的更新

这仍然在发生,但是我已经测试了check_outputcommunicaterun方法的许多组合,现在我对错误在哪里以及如何避免使用有了清楚的了解在Python 3.5和Python 3.6上可以轻松实现。

我的结论:当您在shell=TruePIPEstdout参数上混合使用stderr和任何stdin时会发生这种情况(用于Popenrun方法中)。

请注意: check_output在内部使用PIPE 。 如果您在Python 3.6上查看内部代码,则基本上是使用runstdout=PIPE的调用:https://github.com/python/cpython/blob/ae011e00189d9083dd84c357718264e24fe77314/Lib/subprocess.py#L335

因此,要在Python 3.5或3.6上解决@innisfree问题,只需执行以下操作:

check_output(['sleep', '30'], timeout=1)

对于其他情况,只需避免混合使用shell=TruePIPE,请记住check_output使用PIPE