将模块“子进程”与超时一起使用


325

这是运行任意命令以返回其stdout数据或在非零退出代码上引发异常的Python代码:

proc = subprocess.Popen(
    cmd,
    stderr=subprocess.STDOUT,  # Merge stdout and stderr
    stdout=subprocess.PIPE,
    shell=True)

communicate 用于等待进程退出:

stdoutdata, stderrdata = proc.communicate()

subprocess模块不支持超时-可以杀死运行时间超过X秒的进程-因此,communicate可能需要永远运行。

在打算在Windows和Linux上运行的Python程序中实现超时的最简单方法是什么?


2
一个相关的Python问题跟踪器条目:bugs.python.org/issue5673
Sridhar Ratnakumar,2009年

10
pypi.python.org/pypi/subprocess32用于Python2.x。它是Python 3.x的反向端口。它具有call()和wait()的超时参数。
guettli

1
pypi.python.org/pypi/subprocess32在Windows上不起作用:(
adrianX

Answers:


170

在Python 3.3+中:

from subprocess import STDOUT, check_output

output = check_output(cmd, stderr=STDOUT, timeout=seconds)

output 是一个字节字符串,其中包含命令的合并标准输出,标准错误数据。

check_output提出CalledProcessError问题文本中指定的非零退出状态,这与proc.communicate()的方法。

我已删除,shell=True因为它经常不必要地使用。如果cmd确实需要,您可以随时将其添加回去。如果添加,shell=True即子进程是否产生了自己的后代;check_output()可以比超时指示晚得多返回,请参阅子进程超时失败

超时功能可在Python 2.x上通过subprocess323.2+子进程模块的反向端口使用。


17
实际上,我维护供在Python 2上使用的subprocess32反向端口中
gps

8
@gps Sridhar要求跨平台解决方案,而您的backport仅支持POSIX:当我尝试时,MSVC抱怨(预期)缺少unistd.h :)
Shmil The Cat 2013年

如果不需要输出,则可以使用subprocess.call。
凯尔·吉布森

从Python3.5开始,将subprocess.run()与capture_output = True一起使用,并使用encoding参数获取usefoul输出。
MKesper

1
@MKesper:1- check_output()是获取输出的首选方式(它直接返回输出,不忽略错误,自永远可用。)2- run()更具灵活性,但run()默认情况下会忽略错误,并且需要其他步骤才能获得输出3- check_output()根据实现run(),因此它接受大多数相同的参数。4-尼特:capture_output自3.7起可用,而不是3.5
jfs

205

我对底层细节了解不多;但是,鉴于python 2.6中的API提供了等待线程并终止进程的能力,那么如何在单独的线程中运行进程呢?

import subprocess, threading

class Command(object):
    def __init__(self, cmd):
        self.cmd = cmd
        self.process = None

    def run(self, timeout):
        def target():
            print 'Thread started'
            self.process = subprocess.Popen(self.cmd, shell=True)
            self.process.communicate()
            print 'Thread finished'

        thread = threading.Thread(target=target)
        thread.start()

        thread.join(timeout)
        if thread.is_alive():
            print 'Terminating process'
            self.process.terminate()
            thread.join()
        print self.process.returncode

command = Command("echo 'Process started'; sleep 2; echo 'Process finished'")
command.run(timeout=3)
command.run(timeout=1)

我的计算机中此代码段的输出为:

Thread started
Process started
Process finished
Thread finished
0
Thread started
Process started
Terminating process
Thread finished
-15

从中可以看出,在第一次执行中,进程正确完成了(返回代码0),而在第二次执行中,进程终止了(返回代码-15)。

我没有在Windows中进行测试;但是,除了更新示例命令外,我认为它应该可以工作,因为我在文档中没有发现任何不支持thread.join或process.terminate的内容。


16
+1用于独立于平台。我已经在linux和Windows 7(cygwin和普通Windows python)上运行了它-在所有三种情况下都可以正常工作。
phooji 2011年

7
我已经对您的代码进行了一些修改,以便能够传递本地的Popen kwargs并将其放在要点上。现在可以使用多用途了。gist.github.com/1306188
kirpit

2
对于@redice遇到问题的任何人,此问题可能会有所帮助。简而言之,如果使用shell = True,则shell成为被杀死的子进程,并且其命令(子进程的子进程)继续存在。
Anson 2013年

6
该答案未提供与原始答案相同的功能,因为它不返回标准输出。
stephenbez 2013年

2
thread.is_alive可能导致竞争状态。见ostricher.com/2015/01/python-subprocess-with-timeout
ChaimKut

132

可以使用threading.Timer类简化jcollado的答案:

import shlex
from subprocess import Popen, PIPE
from threading import Timer

def run(cmd, timeout_sec):
    proc = Popen(shlex.split(cmd), stdout=PIPE, stderr=PIPE)
    timer = Timer(timeout_sec, proc.kill)
    try:
        timer.start()
        stdout, stderr = proc.communicate()
    finally:
        timer.cancel()

# Examples: both take 1 second
run("sleep 1", 5)  # process ends normally at 1 second
run("sleep 5", 1)  # timeout happens at 1 second

11
+1为简单的便携式解决方案。您不需要lambdat = Timer(timeout, proc.kill)
jfs

3
+1这应该是公认的答案,因为它不需要更改启动过程的方式。
戴夫·布兰顿

1
为什么需要lambda?没有lambda的情况下不能使用绑定方法p.kill吗?
Danny Staple 2015年

//,您是否愿意提供此用法的示例?
弥敦道(Nathan Basanese),2015年

1
@tuk timer.isAlive()before timer.cancel()表示它正常结束
Charles

83

如果您使用的是Unix,

import signal
  ...
class Alarm(Exception):
    pass

def alarm_handler(signum, frame):
    raise Alarm

signal.signal(signal.SIGALRM, alarm_handler)
signal.alarm(5*60)  # 5 minutes
try:
    stdoutdata, stderrdata = proc.communicate()
    signal.alarm(0)  # reset the alarm
except Alarm:
    print "Oops, taking too long!"
    # whatever else

3
好吧,我对至少在win / linux / mac上有效的跨平台解决方案感兴趣。
Sridhar Ratnakumar,2009年

1
我喜欢这种基于unix的方法。理想情况下,可以将其与特定于Windows的方法(使用CreateProcess和Jobs)结合使用。.但是,到目前为止,下面的解决方案是简单,容易并且可以立即使用的。
Sridhar Ratnakumar,2009年

3
我已经添加了便携式解决方案,请参见我的答案
flybywire

4
此解决方案仅在从主线程调用signal.signal(signal.SIGALARM,alarm_handler)时起作用。请参阅信号文档
volatilevoid

不幸的是,当在Apache模块(例如mod_python,mod_perl或mod_php)的上下文中运行(在Linux上)时,我发现信号和警报的使用被禁止了(大概是因为它们干扰了Apache自己的IPC逻辑)。因此,为了达到使命令超时的目的,我不得不编写“父循环”,该子循环启动一个子进程,然后坐在“睡眠” y循环中监视时钟(并可能还监视子进程的输出)。
彼得

44

这是Alex Martelli作为具有适当过程终止功能的模块的解决方案。其他方法不起作用,因为它们不使用proc.communicate()。因此,如果您有一个产生大量输出的进程,它将填充其输出缓冲区,然后阻塞直到您从中读取内容。

from os import kill
from signal import alarm, signal, SIGALRM, SIGKILL
from subprocess import PIPE, Popen

def run(args, cwd = None, shell = False, kill_tree = True, timeout = -1, env = None):
    '''
    Run a command with a timeout after which it will be forcibly
    killed.
    '''
    class Alarm(Exception):
        pass
    def alarm_handler(signum, frame):
        raise Alarm
    p = Popen(args, shell = shell, cwd = cwd, stdout = PIPE, stderr = PIPE, env = env)
    if timeout != -1:
        signal(SIGALRM, alarm_handler)
        alarm(timeout)
    try:
        stdout, stderr = p.communicate()
        if timeout != -1:
            alarm(0)
    except Alarm:
        pids = [p.pid]
        if kill_tree:
            pids.extend(get_process_children(p.pid))
        for pid in pids:
            # process might have died before getting to this line
            # so wrap to avoid OSError: no such process
            try: 
                kill(pid, SIGKILL)
            except OSError:
                pass
        return -9, '', ''
    return p.returncode, stdout, stderr

def get_process_children(pid):
    p = Popen('ps --no-headers -o pid --ppid %d' % pid, shell = True,
              stdout = PIPE, stderr = PIPE)
    stdout, stderr = p.communicate()
    return [int(p) for p in stdout.split()]

if __name__ == '__main__':
    print run('find /', shell = True, timeout = 3)
    print run('find', shell = True)

3
这在Windows上将不起作用,而且功能的顺序相反。
Hamish Grubijan 2011年

3
当另一个处理程序将自己注册到SIGALARM并在该进程被“杀死”之前终止该进程时,有时会导致异常,这增加了解决方法。顺便说一句,很棒的食谱!到目前为止,我已经使用它启动了50,000个有问题的进程,而不会冻结或破坏处理包装。
Yaroslav Bulatov

如何对其进行修改以在线程化应用程序中运行?我正在尝试从工作线程中使用它并获得ValueError: signal only works in main thread
2011年

@Yaroslav Bulatov感谢您的信息。您为解决上述问题而添加的解决方法是什么?
jpswain 2011年

1
刚刚添加了“ try; catch”块,它位于代码内部。顺便说一句,从长远来看,事实证明这给我带来了问题,因为您只能设置一个SIGALARM处理程序,而其他进程可以重置它。一种解决方法在这里给出- stackoverflow.com/questions/6553423/...
雅罗斯拉夫Bulatov

18

我修改了sussudio答案。现在函数返回:( ,returncodestdoutstderrtimeout - stdoutstderr被解码为UTF-8字符串

def kill_proc(proc, timeout):
  timeout["value"] = True
  proc.kill()

def run(cmd, timeout_sec):
  proc = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  timeout = {"value": False}
  timer = Timer(timeout_sec, kill_proc, [proc, timeout])
  timer.start()
  stdout, stderr = proc.communicate()
  timer.cancel()
  return proc.returncode, stdout.decode("utf-8"), stderr.decode("utf-8"), timeout["value"]

18

惊讶没人提到使用 timeout

timeout 5 ping -c 3 somehost

显然,这不适用于每个用例,但是如果您处理的是简单脚本,那么这是很难克服的。

homebrew对于Mac用户,也可以通过coreutils中的gtimeout 使用。


1
您的意思是:proc = subprocess.Popen(['/usr/bin/timeout', str(timeout)] + cmd, ...)timeoutWindows上是否有OP要求的命令?
jfs 2015年

在Windows中,可以使用git bash之类的应用程序,该应用程序允许Windows中的bash实用程序。
Kaushik Acharya

@KaushikAcharya即使您使用git bash,当python调用子进程时,它将在Windows上运行,因此此绕过将不起作用。
Naman Chikara

16

timeout现在由subprocess模块支持call()communicate()在其中(在Python3.3中):

import subprocess

subprocess.call("command", timeout=20, shell=True)

这将调用命令并引发异常

subprocess.TimeoutExpired

如果20秒后命令仍未完成。

然后,您可以处理异常以继续执行代码,例如:

try:
    subprocess.call("command", timeout=20, shell=True)
except subprocess.TimeoutExpired:
    # insert code here

希望这可以帮助。


有一个提到timeout参数的现有答案。虽然再提一次也不会有伤害。
jfs 2015年

//,我认为OP正在为较旧的Python寻找解决方案。
弥敦道(Nathan Basanese),2015年

11

另一种选择是写入临时文件以防止stdout阻塞,而不是需要使用communication()进行轮询。这对我有用,而其他答案却没有。例如在Windows上。

    outFile =  tempfile.SpooledTemporaryFile() 
    errFile =   tempfile.SpooledTemporaryFile() 
    proc = subprocess.Popen(args, stderr=errFile, stdout=outFile, universal_newlines=False)
    wait_remaining_sec = timeout

    while proc.poll() is None and wait_remaining_sec > 0:
        time.sleep(1)
        wait_remaining_sec -= 1

    if wait_remaining_sec <= 0:
        killProc(proc.pid)
        raise ProcessIncompleteError(proc, timeout)

    # read temp streams from start
    outFile.seek(0);
    errFile.seek(0);
    out = outFile.read()
    err = errFile.read()
    outFile.close()
    errFile.close()

似乎不完整-什么是tempfile?
spiderplant0 2015年

在“ Popen”调用中包含“ import tempfile”,“ import time”和“ shell = True”(请注意“ shell = True”)!
爱德华多·卢西奥

11

我不知道为什么它不mentionned但是因为Python 3.5,有一个新的subprocess.run通用指令(即意味着取代check_callcheck_output......),并且其具有timeout参数也是如此。

subprocess.run(args,*,stdin = None,input = None,stdout = None,stderr = None,shell = False,cwd = None,timeout = None,check = False,encoding = None,errors = None)

Run the command described by args. Wait for command to complete, then return a CompletedProcess instance.

subprocess.TimeoutExpired超时到期时会引发异常。


6

这是我的解决方案,我正在使用线程和事件:

import subprocess
from threading import Thread, Event

def kill_on_timeout(done, timeout, proc):
    if not done.wait(timeout):
        proc.kill()

def exec_command(command, timeout):

    done = Event()
    proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

    watcher = Thread(target=kill_on_timeout, args=(done, timeout, proc))
    watcher.daemon = True
    watcher.start()

    data, stderr = proc.communicate()
    done.set()

    return data, stderr, proc.returncode

实际上:

In [2]: exec_command(['sleep', '10'], 5)
Out[2]: ('', '', -9)

In [3]: exec_command(['sleep', '10'], 11)
Out[3]: ('', '', 0)


5

我将带有线程自的解决方案添加jcollado到了我的Python模块easyprocess中

安装:

pip install easyprocess

例:

from easyprocess import Proc

# shell is not supported!
stdout=Proc('ping localhost').call(timeout=1.5).stdout
print stdout

easyprocess模块​​(code.activestate.com/pypm/easyprocess)为我工作,即使从多处理中使用它也是如此。
iChux

5

如果您使用的是python 2,请尝试一下

import subprocess32

try:
    output = subprocess32.check_output(command, shell=True, timeout=3)
except subprocess32.TimeoutExpired as e:
    print e

1
正如最初的问题所问的那样,可能无法在Windows上运行
Jean-Francois T.

5

前置Linux命令timeout不是一个坏的解决方法,它对我有用。

cmd = "timeout 20 "+ cmd
subprocess.Popen(cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(output, err) = p.communicate()

如何在子流程执行期间打印输出字符串?-子进程返回输出消息。
Ammad

3

我已经实现了我可以从其中一些中学到的东西。这在Windows中有效,并且由于这是社区Wiki,因此我想我也将共享我的代码:

class Command(threading.Thread):
    def __init__(self, cmd, outFile, errFile, timeout):
        threading.Thread.__init__(self)
        self.cmd = cmd
        self.process = None
        self.outFile = outFile
        self.errFile = errFile
        self.timed_out = False
        self.timeout = timeout

    def run(self):
        self.process = subprocess.Popen(self.cmd, stdout = self.outFile, \
            stderr = self.errFile)

        while (self.process.poll() is None and self.timeout > 0):
            time.sleep(1)
            self.timeout -= 1

        if not self.timeout > 0:
            self.process.terminate()
            self.timed_out = True
        else:
            self.timed_out = False

然后从另一个类或文件:

        outFile =  tempfile.SpooledTemporaryFile()
        errFile =   tempfile.SpooledTemporaryFile()

        executor = command.Command(c, outFile, errFile, timeout)
        executor.daemon = True
        executor.start()

        executor.join()
        if executor.timed_out:
            out = 'timed out'
        else:
            outFile.seek(0)
            errFile.seek(0)
            out = outFile.read()
            err = errFile.read()

        outFile.close()
        errFile.close()

实际上,这可能不起作用。该terminate()函数将线程标记为已终止,但实际上并未终止该线程!我可以在* nix中对此进行验证,但是我没有Windows计算机可以测试。
dotancohen

2

一旦您了解了* unix中运行全过程的机器,您将轻松找到更简单的解决方案:

考虑这个简单的示例,如何使用select.select()使超时的communication()方法(现在几乎在* nix上几乎所有可用)。这也可以用epoll / poll / kqueue编写,但是select.select()变体可能是一个很好的例子。而且select.select()的主要限制(速度和最大1024 fds)不适用于您的任务。

这可以在* nix下工作,不创建线程,不使用信号,可以从任何线程(不仅是主线程)启动,并且速度足够快,可以从我的计算机上的stdout(i5 2.3ghz)读取250mb / s的数据。

在通信结束时加入stdout / stderr存在问题。如果您的程序输出很大,可能会导致占用大量内存。但是您可以在较小的超时时间内多次调用communication()。

class Popen(subprocess.Popen):
    def communicate(self, input=None, timeout=None):
        if timeout is None:
            return subprocess.Popen.communicate(self, input)

        if self.stdin:
            # Flush stdio buffer, this might block if user
            # has been writing to .stdin in an uncontrolled
            # fashion.
            self.stdin.flush()
            if not input:
                self.stdin.close()

        read_set, write_set = [], []
        stdout = stderr = None

        if self.stdin and input:
            write_set.append(self.stdin)
        if self.stdout:
            read_set.append(self.stdout)
            stdout = []
        if self.stderr:
            read_set.append(self.stderr)
            stderr = []

        input_offset = 0
        deadline = time.time() + timeout

        while read_set or write_set:
            try:
                rlist, wlist, xlist = select.select(read_set, write_set, [], max(0, deadline - time.time()))
            except select.error as ex:
                if ex.args[0] == errno.EINTR:
                    continue
                raise

            if not (rlist or wlist):
                # Just break if timeout
                # Since we do not close stdout/stderr/stdin, we can call
                # communicate() several times reading data by smaller pieces.
                break

            if self.stdin in wlist:
                chunk = input[input_offset:input_offset + subprocess._PIPE_BUF]
                try:
                    bytes_written = os.write(self.stdin.fileno(), chunk)
                except OSError as ex:
                    if ex.errno == errno.EPIPE:
                        self.stdin.close()
                        write_set.remove(self.stdin)
                    else:
                        raise
                else:
                    input_offset += bytes_written
                    if input_offset >= len(input):
                        self.stdin.close()
                        write_set.remove(self.stdin)

            # Read stdout / stderr by 1024 bytes
            for fn, tgt in (
                (self.stdout, stdout),
                (self.stderr, stderr),
            ):
                if fn in rlist:
                    data = os.read(fn.fileno(), 1024)
                    if data == '':
                        fn.close()
                        read_set.remove(fn)
                    tgt.append(data)

        if stdout is not None:
            stdout = ''.join(stdout)
        if stderr is not None:
            stderr = ''.join(stderr)

        return (stdout, stderr)

2
这仅解决了Unix问题的一半。
Spaceghost 2012年

2

您可以使用 select

import subprocess
from datetime import datetime
from select import select

def call_with_timeout(cmd, timeout):
    started = datetime.now()
    sp = subprocess.Popen(cmd, stdout=subprocess.PIPE)
    while True:
        p = select([sp.stdout], [], [], timeout)
        if p[0]:
            p[0][0].read()
        ret = sp.poll()
        if ret is not None:
            return ret
        if (datetime.now()-started).total_seconds() > timeout:
            sp.kill()
            return None


1

尽管我没有广泛研究它,但我在ActiveState上发现的这种装饰器似乎对这种事情很有用。与一起subprocess.Popen(..., close_fds=True),至少我已经准备好使用Python编写shell脚本了。


该装饰器使用signal.alarm,在Windows上不可用。
dbn

1

如果shell = True,此解决方案将杀死进程树,将参数传递给进程(或不传递参数),具有超时并获取回调的stdout,stderr和进程输出(它将psutil用于kill_proc_tree)。这是基于SO中发布的几种解决方案,包括jcollado的解决方案。在jcollado的回答中张贴对Anson和jradice的评论的回应。已在Windows Srvr 2012和Ubuntu 14.04中测试。请注意,对于Ubuntu,您需要将parent.children(...)调用更改为parent.get_children(...)。

def kill_proc_tree(pid, including_parent=True):
  parent = psutil.Process(pid)
  children = parent.children(recursive=True)
  for child in children:
    child.kill()
  psutil.wait_procs(children, timeout=5)
  if including_parent:
    parent.kill()
    parent.wait(5)

def run_with_timeout(cmd, current_dir, cmd_parms, timeout):
  def target():
    process = subprocess.Popen(cmd, cwd=current_dir, shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)

    # wait for the process to terminate
    if (cmd_parms == ""):
      out, err = process.communicate()
    else:
      out, err = process.communicate(cmd_parms)
    errcode = process.returncode

  thread = Thread(target=target)
  thread.start()

  thread.join(timeout)
  if thread.is_alive():
    me = os.getpid()
    kill_proc_tree(me, including_parent=False)
    thread.join()

1

有一个想法可以继承Popen类并使用一些简单的方法装饰器对其进行扩展。我们称之为ExpirablePopen。

from logging import error
from subprocess import Popen
from threading import Event
from threading import Thread


class ExpirablePopen(Popen):

    def __init__(self, *args, **kwargs):
        self.timeout = kwargs.pop('timeout', 0)
        self.timer = None
        self.done = Event()

        Popen.__init__(self, *args, **kwargs)

    def __tkill(self):
        timeout = self.timeout
        if not self.done.wait(timeout):
            error('Terminating process {} by timeout of {} secs.'.format(self.pid, timeout))
            self.kill()

    def expirable(func):
        def wrapper(self, *args, **kwargs):
            # zero timeout means call of parent method
            if self.timeout == 0:
                return func(self, *args, **kwargs)

            # if timer is None, need to start it
            if self.timer is None:
                self.timer = thr = Thread(target=self.__tkill)
                thr.daemon = True
                thr.start()

            result = func(self, *args, **kwargs)
            self.done.set()

            return result
        return wrapper

    wait = expirable(Popen.wait)
    communicate = expirable(Popen.communicate)


if __name__ == '__main__':
    from subprocess import PIPE

    print ExpirablePopen('ssh -T git@bitbucket.org', stdout=PIPE, timeout=1).communicate()

1

我遇到的问题是,如果花费的时间比给定的超时时间长,我想终止多线程子进程。我想在中设置一个超时Popen(),但是没有用。然后,我意识到这Popen().wait()等于call(),因此我有了在该.wait(timeout=xxx)方法中设置超时的想法,该方法终于奏效了。因此,我通过以下方式解决了问题:

import os
import sys
import signal
import subprocess
from multiprocessing import Pool

cores_for_parallelization = 4
timeout_time = 15  # seconds

def main():
    jobs = [...YOUR_JOB_LIST...]
    with Pool(cores_for_parallelization) as p:
        p.map(run_parallel_jobs, jobs)

def run_parallel_jobs(args):
    # Define the arguments including the paths
    initial_terminal_command = 'C:\\Python34\\python.exe'  # Python executable
    function_to_start = 'C:\\temp\\xyz.py'  # The multithreading script
    final_list = [initial_terminal_command, function_to_start]
    final_list.extend(args)

    # Start the subprocess and determine the process PID
    subp = subprocess.Popen(final_list)  # starts the process
    pid = subp.pid

    # Wait until the return code returns from the function by considering the timeout. 
    # If not, terminate the process.
    try:
        returncode = subp.wait(timeout=timeout_time)  # should be zero if accomplished
    except subprocess.TimeoutExpired:
        # Distinguish between Linux and Windows and terminate the process if 
        # the timeout has been expired
        if sys.platform == 'linux2':
            os.kill(pid, signal.SIGTERM)
        elif sys.platform == 'win32':
            subp.terminate()

if __name__ == '__main__':
    main()

0

不幸的是,我受雇主披露源代码的非常严格的政策约束,因此我无法提供实际的代码。但按我的喜好,最好的解决方案是创建一个重写的子类Popen.wait()以轮询而不是无限期地等待,并Popen.__init__接受超时参数。完成后,所有其他Popen方法(调用wait)将按预期工作,包括communicate


0

https://pypi.python.org/pypi/python-subprocess2提供了子流程模块的扩展,使您可以等待一段时间,否则终止。

因此,要等待10秒钟才能终止进程,否则请终止:

pipe  = subprocess.Popen('...')

timeout =  10

results = pipe.waitOrTerminate(timeout)

这与Windows和UNIX兼容。“结果”是一个字典,它包含“ returnCode”和“ actionTaken”,returnCode是应用程序的返回值(如果必须终止,则为None)。如果该过程正常完成,则显示为“ SUBPROCESS2_PROCESS_COMPLETED”,或者根据执行的操作显示“ SUBPROCESS2_PROCESS_TERMINATED”和SUBPROCESS2_PROCESS_KILLED的掩码(有关详细信息,请参阅文档)


0

对于python 2.6+,请使用gevent

 from gevent.subprocess import Popen, PIPE, STDOUT

 def call_sys(cmd, timeout):
      p= Popen(cmd, shell=True, stdout=PIPE)
      output, _ = p.communicate(timeout=timeout)
      assert p.returncode == 0, p. returncode
      return output

 call_sys('./t.sh', 2)

 # t.sh example
 sleep 5
 echo done
 exit 1

0

python 2.7

import time
import subprocess

def run_command(cmd, timeout=0):
    start_time = time.time()
    df = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    while timeout and df.poll() == None:
        if time.time()-start_time >= timeout:
            df.kill()
            return -1, ""
    output = '\n'.join(df.communicate()).strip()
    return df.returncode, output

-1
import subprocess, optparse, os, sys, re, datetime, threading, time, glob, shutil, xml.dom.minidom, traceback

class OutputManager:
    def __init__(self, filename, mode, console, logonly):
        self.con = console
        self.logtoconsole = True
        self.logtofile = False

        if filename:
            try:
                self.f = open(filename, mode)
                self.logtofile = True
                if logonly == True:
                    self.logtoconsole = False
            except IOError:
                print (sys.exc_value)
                print ("Switching to console only output...\n")
                self.logtofile = False
                self.logtoconsole = True

    def write(self, data):
        if self.logtoconsole == True:
            self.con.write(data)
        if self.logtofile == True:
            self.f.write(data)
        sys.stdout.flush()

def getTimeString():
        return time.strftime("%Y-%m-%d", time.gmtime())

def runCommand(command):
    '''
    Execute a command in new thread and return the
    stdout and stderr content of it.
    '''
    try:
        Output = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True).communicate()[0]
    except Exception as e:
        print ("runCommand failed :%s" % (command))
        print (str(e))
        sys.stdout.flush()
        return None
    return Output

def GetOs():
    Os = ""
    if sys.platform.startswith('win32'):
        Os = "win"
    elif sys.platform.startswith('linux'):
        Os = "linux"
    elif sys.platform.startswith('darwin'):
        Os = "mac"
    return Os


def check_output(*popenargs, **kwargs):
    try:
        if 'stdout' in kwargs: 
            raise ValueError('stdout argument not allowed, it will be overridden.') 

        # Get start time.
        startTime = datetime.datetime.now()
        timeoutValue=3600

        cmd = popenargs[0]

        if sys.platform.startswith('win32'):
            process = subprocess.Popen( cmd, stdout=subprocess.PIPE, shell=True) 
        elif sys.platform.startswith('linux'):
            process = subprocess.Popen( cmd , stdout=subprocess.PIPE, shell=True ) 
        elif sys.platform.startswith('darwin'):
            process = subprocess.Popen( cmd , stdout=subprocess.PIPE, shell=True ) 

        stdoutdata, stderrdata = process.communicate( timeout = timeoutValue )
        retcode = process.poll()

        ####################################
        # Catch crash error and log it.
        ####################################
        OutputHandle = None
        try:
            if retcode >= 1:
                OutputHandle = OutputManager( 'CrashJob_' + getTimeString() + '.txt', 'a+', sys.stdout, False)
                OutputHandle.write( cmd )
                print (stdoutdata)
                print (stderrdata)
                sys.stdout.flush()
        except Exception as e:
            print (str(e))

    except subprocess.TimeoutExpired:
            ####################################
            # Catch time out error and log it.
            ####################################
            Os = GetOs()
            if Os == 'win':
                killCmd = "taskkill /FI \"IMAGENAME eq {0}\" /T /F"
            elif Os == 'linux':
                killCmd = "pkill {0)"
            elif Os == 'mac':
                # Linux, Mac OS
                killCmd = "killall -KILL {0}"

            runCommand(killCmd.format("java"))
            runCommand(killCmd.format("YouApp"))

            OutputHandle = None
            try:
                OutputHandle = OutputManager( 'KillJob_' + getTimeString() + '.txt', 'a+', sys.stdout, False)
                OutputHandle.write( cmd )
            except Exception as e:
                print (str(e))
    except Exception as e:
            for frame in traceback.extract_tb(sys.exc_info()[2]):
                        fname,lineno,fn,text = frame
                        print "Error in %s on line %d" % (fname, lineno)

这是可憎的
Corey Goldberg

-2

只是想写一些简单的东西。

#!/usr/bin/python

from subprocess import Popen, PIPE
import datetime
import time 

popen = Popen(["/bin/sleep", "10"]);
pid = popen.pid
sttime = time.time();
waittime =  3

print "Start time %s"%(sttime)

while True:
    popen.poll();
    time.sleep(1)
    rcode = popen.returncode
    now = time.time();
    if [ rcode is None ]  and  [ now > (sttime + waittime) ] :
        print "Killing it now"
        popen.kill()

time.sleep(1)是一个非常糟糕的主意-假设您要运行许多需要花费0.002秒的命令。您应该等待poll()(请参阅select,对于Linux epol推荐:)
ddzialak 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.