Using module 'subprocess' with timeout

后端 未结 29 2433
旧巷少年郎
旧巷少年郎 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 16:10

    If you're on 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
    
    0 讨论(0)
  • 2020-11-21 16:10

    I've modified sussudio answer. Now function returns: (returncode, stdout, stderr, timeout) - stdout and stderr is decoded to utf-8 string

    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"]
    
    0 讨论(0)
  • 2020-11-21 16:11

    Here is my solution, I was using Thread and Event:

    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 action:

    In [2]: exec_command(['sleep', '10'], 5)
    Out[2]: ('', '', -9)
    
    In [3]: exec_command(['sleep', '10'], 11)
    Out[3]: ('', '', 0)
    
    0 讨论(0)
  • 2020-11-21 16:11

    I've used killableprocess successfully on Windows, Linux and Mac. If you are using Cygwin Python, you'll need OSAF's version of killableprocess because otherwise native Windows processes won't get killed.

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

    I don't know much about the low level details; but, given that in python 2.6 the API offers the ability to wait for threads and terminate processes, what about running the process in a separate thread?

    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)
    

    The output of this snippet in my machine is:

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

    where it can be seen that, in the first execution, the process finished correctly (return code 0), while the in the second one the process was terminated (return code -15).

    I haven't tested in windows; but, aside from updating the example command, I think it should work since I haven't found in the documentation anything that says that thread.join or process.terminate is not supported.

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

    surprised nobody mentioned using timeout

    timeout 5 ping -c 3 somehost

    This won't for work for every use case obviously, but if your dealing with a simple script, this is hard to beat.

    Also available as gtimeout in coreutils via homebrew for mac users.

    0 讨论(0)
提交回复
热议问题