Hier ist der Python-Code, mit dem Sie einen beliebigen Befehl ausführen können, der seine stdout
Daten zurückgibt, oder eine Ausnahme für Exit-Codes ungleich Null auslösen:
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
Modul unterstützt kein Timeout - die Fähigkeit, einen Prozess abzubrechen, der länger als X Sekunden ausgeführt wird - kann daher communicate
ewig dauern.
Was ist der einfachste Weg, um Zeitüberschreitungen in einem Python-Programm zu implementieren, das unter Windows und Linux ausgeführt werden soll?
python
multithreading
timeout
subprocess
Sridhar Ratnakumar
quelle
quelle
Antworten:
In Python 3.3+:
output
ist eine Byte-Zeichenfolge, die die zusammengeführten stdout- und stderr-Daten des Befehls enthält.check_output
Erhöht denCalledProcessError
Exit-Status ungleich Null, wie im Text der Frage angegeben, im Gegensatz zurproc.communicate()
Methode.Ich habe entfernt,
shell=True
weil es oft unnötig verwendet wird. Sie können es jederzeit wieder hinzufügen, wenn diescmd
tatsächlich erforderlich ist. Wenn Sie hinzufügen,shell=True
dh wenn der untergeordnete Prozess seine eigenen Nachkommen erzeugt;check_output()
kann viel später als im Zeitlimit angegeben zurückgegeben werden, siehe Fehler beim Zeitlimit für Unterprozesse .Die Timeout-Funktion ist in Python 2.x über den
subprocess32
Backport des Subprozessmoduls 3.2+ verfügbar .quelle
check_output()
ist der bevorzugte Weg, um eine Ausgabe zu erhalten (es gibt die Ausgabe direkt zurück, ignoriert keine Fehler, es ist seit Ewigkeiten verfügbar). 2-run()
ist flexibler,run()
ignoriert jedoch standardmäßig Fehler und erfordert zusätzliche Schritte, um die Ausgabe zu erhalten. 3-check_output()
ist in Bezug auf implementiertrun()
und akzeptiert daher die meisten der gleichen Argumente. 4-nit:capture_output
ist verfügbar seit 3.7, nicht 3.5Ich weiß nicht viel über die Details auf niedriger Ebene. Angesichts der Tatsache, dass die API in Python 2.6 die Möglichkeit bietet, auf Threads zu warten und Prozesse zu beenden, können Sie den Prozess in einem separaten Thread ausführen.
Die Ausgabe dieses Snippets in meinem Computer ist:
wo zu sehen ist, dass in der ersten Ausführung der Prozess korrekt beendet wurde (Rückkehrcode 0), während in der zweiten der Prozess beendet wurde (Rückkehrcode -15).
Ich habe nicht in Windows getestet. Abgesehen von der Aktualisierung des Beispielbefehls sollte dies funktionieren, da ich in der Dokumentation nichts gefunden habe, was besagt, dass thread.join oder process.terminate nicht unterstützt wird.
quelle
Die Antwort von jcollado kann mithilfe der threading.Timer- Klasse vereinfacht werden :
quelle
lambda
:t = Timer(timeout, proc.kill)
timer.isAlive()
vortimer.cancel()
bedeutet, dass es normal endeteWenn Sie unter Unix sind,
quelle
Hier ist Alex Martellis Lösung als Modul mit ordnungsgemäßem Prozessabbruch. Die anderen Ansätze funktionieren nicht, da sie nicht proc.communicate () verwenden. Wenn Sie also einen Prozess haben, der viel Ausgabe erzeugt, füllt er seinen Ausgabepuffer und blockiert dann, bis Sie etwas daraus lesen.
quelle
ValueError: signal only works in main thread
Ich habe die Sussudio- Antwort geändert . Nun Funktion gibt: (
returncode
,stdout
,stderr
,timeout
) -stdout
undstderr
decodiert , um utf-8 - Stringquelle
überrascht niemand erwähnt mit
timeout
timeout 5 ping -c 3 somehost
Dies ist natürlich nicht für jeden Anwendungsfall geeignet, aber wenn Sie mit einem einfachen Skript arbeiten, ist dies schwer zu übertreffen.
Auch als Gtimeout in Coreutils über
homebrew
für Mac-Benutzer verfügbar .quelle
proc = subprocess.Popen(['/usr/bin/timeout', str(timeout)] + cmd, ...)
. Gibt estimeout
unter Windows einen Befehl, wenn OP danach fragt?timeout
wird jetzt voncall()
undcommunicate()
im Unterprozessmodul (ab Python3.3) unterstützt:Dadurch wird der Befehl aufgerufen und die Ausnahme ausgelöst
wenn der Befehl nach 20 Sekunden nicht beendet wird.
Sie können dann die Ausnahme behandeln, um Ihren Code fortzusetzen, etwa:
Hoffe das hilft.
quelle
timeout
Parameter erwähnt . Noch einmal zu erwähnen würde nicht schaden.Eine andere Möglichkeit besteht darin, in eine temporäre Datei zu schreiben, um die Blockierung von stdout zu verhindern, anstatt mit communic () abfragen zu müssen. Dies funktionierte für mich, wo die anderen Antworten nicht funktionierten; zum Beispiel unter Windows.
quelle
Ich weiß nicht, warum es nicht erwähnt wird, aber seit Python 3.5 gibt es einen neuen
subprocess.run
universellen Befehl (der ersetzen sollcheck_call
,check_output
...), der auch dentimeout
Parameter enthält.Es wird eine
subprocess.TimeoutExpired
Ausnahme ausgelöst, wenn das Zeitlimit abgelaufen ist.quelle
Hier ist meine Lösung, ich habe Thread und Event verwendet:
In Aktion:
quelle
Die Lösung, die ich verwende, besteht darin, dem Shell-Befehl ein Zeitlimit voranzustellen . Wenn der Befehl zu lange dauert, stoppt ihn timelimit und Popen hat einen durch timelimit festgelegten Rückkehrcode. Wenn es> 128 ist, bedeutet dies, dass das Zeitlimit den Prozess beendet hat.
Siehe auch Python-Unterprozess mit Timeout und großer Ausgabe (> 64 KB)
quelle
timeout
- packages.ubuntu.com/search?keywords=timeout - aber keines funktioniert unter Windows, oder?Ich habe die Lösung mit Threading von
jcollado
zu meinem Python-Modul easyprocess hinzugefügt .Installieren:
Beispiel:
quelle
Wenn Sie Python 2 verwenden, probieren Sie es aus
quelle
Das Voranstellen des Linux-Befehls
timeout
ist keine schlechte Problemumgehung und hat bei mir funktioniert.quelle
Ich habe implementiert, was ich aus einigen davon gewinnen konnte. Dies funktioniert unter Windows, und da dies ein Community-Wiki ist, würde ich wahrscheinlich auch meinen Code freigeben:
Dann aus einer anderen Klasse oder Datei:
quelle
terminate()
Funktion markiert einen Thread als beendet, beendet den Thread jedoch nicht! Ich kann dies in * nix überprüfen, habe aber keinen Windows-Computer zum Testen.Sobald Sie die vollständigen Prozesslaufmaschinen in * unix verstanden haben, werden Sie leicht eine einfachere Lösung finden:
Betrachten Sie dieses einfache Beispiel, wie Sie mithilfe von select.select () ein Timeout für die Kommunikation () meth erstellen können (heutzutage fast überall auf * nix verfügbar). Dies kann auch mit epoll / poll / kqueue geschrieben werden, aber die Variante select.select () könnte ein gutes Beispiel für Sie sein. Die Hauptbeschränkungen von select.select () (Geschwindigkeit und maximal 1024 fds) sind für Ihre Aufgabe nicht anwendbar.
Dies funktioniert unter * nix, erstellt keine Threads, verwendet keine Signale, kann von jedem Thread (nicht nur von main) gestartet werden und reicht schnell aus, um 250 MB / s Daten von stdout auf meinem Computer zu lesen (i5 2,3 GHz).
Es gibt ein Problem beim Beitritt zu stdout / stderr am Ende der Kommunikation. Wenn Sie eine große Programmausgabe haben, kann dies zu einer großen Speichernutzung führen. Sie können communic () jedoch mehrmals mit kleineren Zeitüberschreitungen aufrufen.
quelle
Sie können dies mit tun
select
quelle
Ich habe killableprocess erfolgreich unter Windows, Linux und Mac verwendet. Wenn Sie Cygwin Python verwenden, benötigen Sie die OSAF-Version von killableprocess, da sonst native Windows-Prozesse nicht beendet werden.
quelle
Obwohl ich es mir nicht ausführlich angesehen habe, scheint dieser Dekorateur, den ich bei ActiveState gefunden habe, für solche Dinge sehr nützlich zu sein. Zusammen mit
subprocess.Popen(..., close_fds=True)
zumindest bin ich bereit für die Shell-Scripting in Python.quelle
Diese Lösung beendet den Prozessbaum im Fall von shell = True, übergibt Parameter an den Prozess (oder nicht), hat eine Zeitüberschreitung und ruft die Ausgabe stdout, stderr und process des Rückrufs ab (sie verwendet psutil für kill_proc_tree). Dies basierte auf mehreren in SO veröffentlichten Lösungen, einschließlich jcollados. Posting als Antwort auf Kommentare von Anson und Jradice in der Antwort von jcollado. Getestet in Windows Srvr 2012 und Ubuntu 14.04. Bitte beachten Sie, dass Sie für Ubuntu den Aufruf parent.children (...) in parent.get_children (...) ändern müssen.
quelle
Es gibt eine Idee, die Popen-Klasse zu unterordnen und sie mit einigen einfachen Methodendekoratoren zu erweitern. Nennen wir es ExpiringPopen.
quelle
Ich hatte das Problem, dass ich einen Multithreading-Unterprozess beenden wollte, wenn er länger als eine bestimmte Zeitüberschreitungsdauer dauerte. Ich wollte eine Zeitüberschreitung festlegen
Popen()
, aber es hat nicht funktioniert. Dann wurde mir klar, dass diesPopen().wait()
gleich ist,call()
und so kam mir die Idee, eine Zeitüberschreitung innerhalb der.wait(timeout=xxx)
Methode festzulegen , die schließlich funktionierte. Also habe ich es so gelöst:quelle
Leider bin ich an sehr strenge Richtlinien zur Offenlegung des Quellcodes durch meinen Arbeitgeber gebunden, sodass ich keinen tatsächlichen Code bereitstellen kann. Für meinen Geschmack besteht die beste Lösung darin, eine Unterklasse zu erstellen, die überschreibt,
Popen.wait()
um abzufragen, anstatt auf unbestimmte Zeit zu warten, undPopen.__init__
einen Timeout-Parameter zu akzeptieren. Sobald Sie dies tun, funktionieren alle anderenPopen
Methoden (die aufrufenwait
) wie erwartet, einschließlichcommunicate
.quelle
https://pypi.python.org/pypi/python-subprocess2 bietet Erweiterungen für das Unterprozessmodul, mit denen Sie bis zu einem bestimmten Zeitraum warten können, andernfalls beenden.
Warten Sie also bis zu 10 Sekunden, bis der Vorgang beendet ist. Andernfalls beenden Sie Folgendes:
Dies ist sowohl mit Windows als auch mit Unix kompatibel. "results" ist ein Wörterbuch, das "returnCode" enthält, dh die Rückgabe der App (oder None, wenn sie beendet werden musste) sowie "actionTaken". Dies ist "SUBPROCESS2_PROCESS_COMPLETED", wenn der Prozess normal abgeschlossen wurde, oder eine Maske aus "SUBPROCESS2_PROCESS_TERMINATED" und SUBPROCESS2_PROCESS_KILLED, abhängig von den ergriffenen Maßnahmen (Einzelheiten finden Sie in der Dokumentation).
quelle
Verwenden Sie für Python 2.6+ gevent
quelle
Python 2.7
quelle
quelle
Ich habe nur versucht, etwas Einfacheres zu schreiben.
quelle