subprocess -> wann beendet?

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Ich starte ein Kommando mittels:

Code: Alles auswählen

process = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Ich möchte einen Timeout festlegen, um den Sub-Prozess ggf. automatisch zu beenden, wenn er z.B. nach 15 Sekunden nicht "antwortet". Wie aber kann ich erkennen, ob der Sub-Prozess fertig ist, also beendet wurde? Habe es mit dem `returncode` versucht, aber es gibt ja durchaus Kommandos (z.B. `dir` unter Windows oder `ls` unter Linux), die keinen Returnwert liefern. Dann gibt es da noch die `poll()` Funktion, aber die reagiert auch nicht unbedingt... die Dokumentation verschweigt da vllt. ein paar wichtige Details.

Kann mir wer unter die Arme greifen?
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Die von dir verlinkte Doku sagt doch, dass `returncode`ein `None` liefert, wenn der Prozess noch nicht beendet wurde oder ansonsten eben ein `int` mit dem Returncode. Das sollte doch zur Unterscheidung reichen, oder?

Stefan
BlackJack

@droptix: Und *jedes* Programm, das sich beendet, gibt einen Returncode zurück, inklusive ``dir`` und ``ls``.
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Jeder Prozess gibt einen Returncode zurueck. Bei shell=True bekommst du den returncode von der Shell.

Mmh, bei mir steht in returncode erst was drin, wenn ich poll aufgerufen habe:

Code: Alles auswählen

>>> p = subprocess.Popen("/bin/ls")
einfuehrung.pdf  einfuehrung.tex
>>> p.returncode
>>> p.returncode
>>> p.returncode
>>> p.returncode
>>> p.returncode
>>> p.poll()
0
>>> p.returncode
0

Code: Alles auswählen

>>> p = subprocess.Popen("echo $HOME", shell=True)
/home/rbreu
>>> p.returncode
>>> p.returncode
>>> p.returncode
>>> p.returncode
>>> p.returncode
>>> p.returncode
>>> p.poll()
0
>>> p.returncode
0
Macht natuerlich sinn, dass man aktiv pollen muss, aber das geht nicht so direktk aus der Doku hervor.
Offizielles Python-Tutorial (Deutsche Version)

Urheberrecht, Datenschutz, Informationsfreiheit: Piratenpartei
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Ah genau, das scheint es zu sein. Ohne `poll()` bleibt `returncode` u.U. immer auf `None`. Nun geht's, super! :D
Benutzeravatar
keppla
User
Beiträge: 483
Registriert: Montag 31. Oktober 2005, 00:12

Anstatt poll lieber explizit aufs ende des prozesses (mit .wait()) warten, das scheint das gleiche zu sein.
BlackJack

@keppla: `wait()` blockiert aber. Die Aufgabenstellung war ja nach x Sekunden nachsehen ob Prozess fertig, wenn nein -> töten.
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Genau!

Noch ein Problem: Unter Windows bekomme ich mittels so eine falsche PID zurück:

Code: Alles auswählen

process = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
print process.pid
Geprinted wird z.B. 1740, der TaskManager zeigt aber 2112. Wie das?

Unter Linux muss ich das erst noch testen...
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Aaaah, es wird die PID der Shell zurück gegeben. So muss man das machen:

Code: Alles auswählen

process = subprocess.Popen(cmd, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
print process.pid
[Edit:] Nein, das ist aber auch Mist! So kann ich keine Shell-Befehle wie `dir` mehr ausführen. Da kommt dann sofort dieser Fehler hier:

<type 'exceptions.WindowsError'>:[Error 2] Das System kann die angegebene Datei nicht finden

Also muss ich `shell=True` lassen, dann klappen zumindest alle Befehle... aber ich kann ein GUI-Programm so z.B. nicht nach einem Timeout beenden. Jemand ne Idee dazu?
Benutzeravatar
keppla
User
Beiträge: 483
Registriert: Montag 31. Oktober 2005, 00:12

BlackJack hat geschrieben:@keppla: `wait()` blockiert aber. Die Aufgabenstellung war ja nach x Sekunden nachsehen ob Prozess fertig, wenn nein -> töten.
Uff, sorry, hab das irgendwie auf "was ist der returncode" reduziert. :oops:
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Anscheinend ist dir (im Gegensatz zu ls unter Linux) kein alleinstehendes Programm, sondern eine Shell-Anweisung.

Warum moechtest du ueberhaupt aus Python heraus Shell-Anweisungen ausfuehren? Immerhin programmierst du ja Python... :wink:
Offizielles Python-Tutorial (Deutsche Version)

Urheberrecht, Datenschutz, Informationsfreiheit: Piratenpartei
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

Rebecca hat geschrieben:Warum moechtest du ueberhaupt aus Python heraus Shell-Anweisungen ausfuehren?
Weil es geht... nein ernsthaft: ich bastle eine einfache Python-Shell, um auf einen entfernten Rechner zugreifen zu können.

Idee von mir: alle Prozesse beenden, die als Parent-PID die zurück gegebene PID (also die der Shell) besitzen. Das könnte vielleicht klappen...
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

droptix hat geschrieben:Ich möchte einen Timeout festlegen, um den Sub-Prozess ggf. automatisch zu beenden, wenn er z.B. nach 15 Sekunden nicht "antwortet".
Ich hab sowas mal gemacht, siehe:
http://www.python-forum.de/post-23692.html#23692
http://www.pylucid.net:8080/pylucid/bro ... ls.py#L477

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

jens hat geschrieben:Ich hab sowas mal gemacht
Danke, aber dient mir leider überhaupt nicht. Ich will ja nicht abwarten, bis der Prozess tatsächlich gekillt wurde und hinterher darauf reagieren. Außerdem ist es komisch zu sagen "Wenn der Prozess 30 Sekunden dauerte und dann mit returncode 0 (= i.d.R. sauberer Exit ohne Fehler) endete, dann wurde er wahrscheinlich gekillt." -> stimmt ja höchst wahrscheinlich nicht und ist total vom Prozess abhängig.

Ich möchte den Prozess gewaltsam beenden, wenn er mir zu lange dauert (oder eben wirklich hängt).
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Der sourcecode ist schon alt... Der Prozess wird gewaltsam gekillt, nachdem der timeout überschritten wird.

EDIT: Aber:
Da os.kill() nur unter Linux und Mac verfügbar ist, funktioniert das
ganze nicht unter Windows :(

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Hab das ganze nochmal überarbeitet...
http://trac.pylucid.net/browser/trunk/p ... y?rev=1634

Dabei ist zu beachten, das man stdout und stderr nur dann lesen kann, wenn der Prozess normal beendet wurde. Wurde der Process abgeschossen, blockiert das .read()...

Eine Idee wie man das umgehen kann???

EDIT: Link aktualisiert.
Zuletzt geändert von jens am Freitag 31. Oktober 2008, 15:20, insgesamt 1-mal geändert.

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

lunar hat geschrieben:SIGTERM nutzen.
Warum, kannst du das näher erläutern? Ich kenne mich da nicht so richtig aus...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
droptix
User
Beiträge: 521
Registriert: Donnerstag 13. Oktober 2005, 21:27

jens hat geschrieben:Wurde der Process abgeschossen, blockiert das .read()...
Das Problem habe ich auch. Ich umgehe das einfach damit, dass ich .read() erst durchführe, wenn .returncode nicht gleich None ist (andernfalls greift sowieso der Timeout und killt den Prozess). Ich weiß aber nicht, wie viel Output dabei gepuffert wird.

Ich mache übrigens noch eine kleine Unterscheidung in Windows und Unix/Mac:

Code: Alles auswählen

TIMEOUT = 3
PLATFORM = sys.platform

if PLATFORM == "win32":
	import win32api
	import win32con
	import win32process
else:
	import os
	import signal

# ... somewhere else:

			process = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
			t = time.time()
			while True:
				process.poll()
				if process.returncode is not None:
					output = process.stdout.read()
					returnCode = process.returncode
					break
				elif time.time() - t >= TIMEOUT:
					# try to kill process
					try:
						if PLATFORM == "win32":
							try:
								h = win32api.OpenProcess(win32con.PROCESS_ALL_ACCESS, False, process.pid)
								handle = h.handle
								win32process.TerminateProcess(handle, 1)
								killed = process.pid
							except Exception, e:
								killed = e
						else:
							try:
								os.kill(process.pid, signal.SIGUSR1)
								killed = process.pid
							except Exception, e:
								killed = e
					finally:
						break
				time.sleep(0.5)
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

jens hat geschrieben:
lunar hat geschrieben:SIGTERM nutzen.
Warum, kannst du das näher erläutern? Ich kenne mich da nicht so richtig aus...
Es gibt ja viele verschiedene Signale. SIGTERM (15) ist quasi die freundliche Bitte an den Prozess, sich ordnungsgemaess zu beenden. Der Prozess kann dann noch aufraeumen, koennte das Signal sogar ignorieren. IMO wird z.B. ein SIGTERM gesendet, wenn du im Fenster-Titel auf das "x" clickst.

SIGKILL (9) hingegen ist ein hartes, sofortiges Abschiessen des Prozesses und sollte nur verwendet werden, wenn SIGTERM nicht mehr hilft.
Offizielles Python-Tutorial (Deutsche Version)

Urheberrecht, Datenschutz, Informationsfreiheit: Piratenpartei
Antworten