Hier ist der Python-Code, um einen beliebigen Befehl auszuführen, der seinen zurückgibt stdout
Daten, oder lösen Sie eine Ausnahme bei Exit-Codes ungleich Null aus:
proc = subprocess.Popen(
cmd,
stderr=subprocess.STDOUT, # Merge stdout and stderr
stdout=subprocess.PIPE,
shell=True)
communicate
wird verwendet, um auf das Beenden des Prozesses zu warten:
stdoutdata, stderrdata = proc.communicate()
Das subprocess
Das Modul unterstützt kein Timeout – die Fähigkeit, einen Prozess zu beenden, der länger als X Sekunden läuft – daher communicate
kann ewig dauern.
Was ist der einfachste Möglichkeit, Timeouts in einem Python-Programm zu implementieren, das unter Windows und Linux ausgeführt werden soll?
Ich weiß nicht viel über die Details auf niedriger Ebene; aber angesichts der Tatsache, dass die API in Python 2.6 die Möglichkeit bietet, auf Threads zu warten und Prozesse zu beenden, was ist mit der Ausführung des Prozesses in einem separaten 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)
Die Ausgabe dieses Snippets in meinem Computer ist:
Thread started
Process started
Process finished
Thread finished
0
Thread started
Process started
Terminating process
Thread finished
-15
wo zu sehen ist, dass bei der ersten Ausführung der Prozess korrekt beendet wurde (Rückgabecode 0), während bei der zweiten der Prozess beendet wurde (Rückgabecode -15).
Ich habe nicht in Windows getestet; Aber abgesehen von der Aktualisierung des Beispielbefehls denke ich, dass es funktionieren sollte, da ich in der Dokumentation nichts gefunden habe, was besagt, dass thread.join oder process.terminate nicht unterstützt werden.
Wenn Sie Unix verwenden,
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
Hier ist die Lösung von Alex Martelli als Modul mit ordnungsgemäßer Prozessbeseitigung. Die anderen Ansätze funktionieren nicht, weil sie proc.communicate() nicht verwenden. Wenn Sie also einen Prozess haben, der viele Ausgaben erzeugt, füllt er seinen Ausgabepuffer und blockiert dann, bis Sie etwas daraus lesen.
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)
timeout
wird jetzt unterstützt durch call()
und communicate()
im subprocess-Modul (ab Python3.3):
import subprocess
subprocess.call("command", timeout=20, shell=True)
Dadurch wird der Befehl aufgerufen und die Ausnahme ausgelöst
subprocess.TimeoutExpired
wenn der Befehl nach 20 Sekunden nicht beendet ist.
Sie können dann die Ausnahme behandeln, um Ihren Code fortzusetzen, etwa so:
try:
subprocess.call("command", timeout=20, shell=True)
except subprocess.TimeoutExpired:
# insert code here
Hoffe das hilft.
10947700cookie-checkVerwenden des Moduls ‘subprocess’ mit Timeoutyes
Ein verwandter Eintrag in der Python-Problemverfolgung: bugs.python.org/issue5673
– Sridhar Ratnakumar
28. Juli 2009 um 1:54 Uhr
Verwenden pypi.python.org/pypi/subprocess32 für Python2.x. Es ist ein Backport von Python 3.x. Es hat das Timeout-Argument für call() und wait().
– Güttli
18. November 2013 um 12:28 Uhr
pypi.python.org/pypi/subprocess32 funktioniert nicht unter Windows 🙁
– AdrianX
9. November 2015 um 1:13 Uhr