Using module 'subprocess' with timeout

后端 未结 29 2392
旧巷少年郎
旧巷少年郎 2020-11-21 15:15

Here\'s the Python code to run an arbitrary command returning its stdout data, or raise an exception on non-zero exit codes:

proc = subprocess.P         


        
相关标签:
29条回答
  • 2020-11-21 15:51

    Unfortunately, I'm bound by very strict policies on the disclosure of source code by my employer, so I can't provide actual code. But for my taste the best solution is to create a subclass overriding Popen.wait() to poll instead of wait indefinitely, and Popen.__init__ to accept a timeout parameter. Once you do that, all the other Popen methods (which call wait) will work as expected, including communicate.

    0 讨论(0)
  • 2020-11-21 15:52

    I don't know why it isn't mentionned but since Python 3.5, there's a new subprocess.run universal command (that is meant to replace check_call, check_output ...) and which has the timeout parameter as well.

    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.
    

    It raises a subprocess.TimeoutExpired exception when the timeout is expired.

    0 讨论(0)
  • 2020-11-21 15:54

    timeout is now supported by call() and communicate() in the subprocess module (as of Python3.3):

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

    This will call the command and raise the exception

    subprocess.TimeoutExpired
    

    if the command doesn't finish after 20 seconds.

    You can then handle the exception to continue your code, something like:

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

    Hope this helps.

    0 讨论(0)
  • 2020-11-21 15:56

    This solution kills the process tree in case of shell=True, passes parameters to the process (or not), has a timeout and gets the stdout, stderr and process output of the call back (it uses psutil for the kill_proc_tree). This was based on several solutions posted in SO including jcollado's. Posting in response to comments by Anson and jradice in jcollado's answer. Tested in Windows Srvr 2012 and Ubuntu 14.04. Please note that for Ubuntu you need to change the parent.children(...) call to 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()
    
    0 讨论(0)
  • 2020-11-21 15:58

    jcollado's answer can be simplified using the threading.Timer class:

    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
    
    0 讨论(0)
  • 2020-11-21 15:58

    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
    
    0 讨论(0)
提交回复
热议问题