Skript aus thread heraus beenden

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.
BlackJack

@snafu: Der ganze Aufruf mit der Pipe ist Unsinn weil es dafür eine Funktion in der Standardbibliothek gibt, die nicht nur deutlich robuster funktioniert weil sie einfach die entsprechende POSIX C-Funktion aufruft um die eigene PID zu ermitteln, sondern auch auf System wo es ``ps``, ``grep``, oder ``awk`` nicht gibt. Das was da Prozess-ID genannt wird kann letztendlich deutlich mehr sein weil ``ps aux`` (zumindest unter Linux) auch die Kommandozeilenargumente zu jedem Prozess ausgibt, und das ``grep`` *alles* findet wo 'skriptname' in der Zeile enthalten ist. Unter anderen auch *sich selbst*. m)

Code: Alles auswählen

$ ps aux | grep forum5.py | awk '{ print $2 }'
5928
5937
$ ps aux | grep forum5.py 
bj        5928  0.1  0.2  30268 11788 pts/5    Sl+  17:57   0:00 python forum5.py
bj        5940  0.0  0.0   4388   816 pts/6    S+   17:57   0:00 grep --color=auto forum5.py
Das ist, äh, suboptimal. ;-) Typischer Shell-Skripting-Hack den man öfter sieht, ist da noch ein ``| grep -v grep`` hinten dran zu hängen. Dann muss man aber sicher sein das das in der Zeile die man erwischen möchte garantiert nicht vorkommt, weder als Teilzeichenkette im Namen selbst, noch irgendwo in den Kommandozeilenargumenten.
Benutzeravatar
mobby
User
Beiträge: 76
Registriert: Donnerstag 17. April 2014, 09:43

Ich löse mein Probelm jetzt einfach ruppig, indem ich einfach im Thread folgendes mache:

Code: Alles auswählen

pid = str(os.getpid())
subprocess.call(["kill -9 " + pid], shell=True)
Funktioniert ...
BlackJack

@mobby: Wo Du doch Zeile 1 schon ohne `subprocess` hast, könntest Du das mit Zeile 2 vielleicht auch machen…
Benutzeravatar
mobby
User
Beiträge: 76
Registriert: Donnerstag 17. April 2014, 09:43

@BlackJack: So besser?

Code: Alles auswählen

import os
import signal

pid = os.getpid()
os.kill(pid, signal.SIGKILL)
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@mobby:
Willst Du ernsthaft den Kontrollfluß über den Kernel steuern? Also ich ruf nicht beim Hausmeister an, ob er mal kurz den Strom abstellen könnte, nur weil die Waschmaschine noch an ist. :wink:
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

@jerch: und neben Deiner Waschmaschine steht eine Brechstange, weil anders die Tür nicht aufgeht. :twisted:
Benutzeravatar
mobby
User
Beiträge: 76
Registriert: Donnerstag 17. April 2014, 09:43

Kleiner Nachtrag:

1. Mir ist aufgefallen, dass wenn ich mein Skript über einen watchdog bzw. generell im Hintergrund starte, das Skript zwar beendet wird, allerdings nicht in die Befehlszeile der Konsole zurückgekehrt wird. Bzw. Vielleicht läuft es sogar noch, ist nicht zu erkennen. Woran liegt das und wie kann ich es beheben?

2. Ist zwar etwas OT aber bevor ich schon wieder n neuen Thread starte ... In meinem Skript wird eine Email verschickt, dieser Vorgang spielt sich irgendwie jedes mal in meiner Konsole ab. Das will ich aber nicht, wie behebe ich auch das?

Code: Alles auswählen

import smtplib
import os
import signal
import MySQLdb as db
from contextlib import closing
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText


def send_email(content, mysql_error_code):
    fromaddr = ""
    toaddr = ""
    msg = MIMEMultipart()
    msg['From'] = fromaddr
    msg['To'] = toaddr
    msg['Subject'] = "MySQL Error: " + mysql_error_code
    msg.attach(MIMEText(content))
    text = msg.as_string()
    server = smtplib.SMTP('', 25)
    server.set_debuglevel(1)
    server.ehlo()
    server.starttls()
    server.login('','')
    server.sendmail(fromaddr, toaddr, text)
    server.quit()


def kill_script():
    pid = os.getpid()
    os.kill(pid, signal.SIGKILL) 


def insert_into_database(timestamp, datum, uhrzeit, energy_cam_value):
    try:
        with closing(
            db.connect(
		    host='',
	 	    user='', 
		    passwd='', 
		    db='')
        ) as connection:
            connection.cursor().execute(
                'INSERT INTO EC_Zaehler_01(Zeitstempel, Datum, Zeit, Value) VALUES (%s, %s, %s, %s)',
                (timestamp, datum, uhrzeit, energy_cam_value)
            )
            connection.commit()
    except db.Error, e:
        mysql_error_code = str(e.args[0])
        mysql_error_message = str(e.args[1])
        content = "Am " + datum + " ist um " + uhrzeit + " ein Fehler aufgetreten. \nEs konnte keine Verbindung zum Server hergestellt werden. \n\nFehlermeldung:\n" + mysql_error_message
        send_email(content, mysql_error_code)
        kill_script()


def main():
    insert_into_database("123456789", "01:01:1990", "00:00:00",  "100")


if __name__ == '__main__':
    main()
BlackJack

@mobby: Ad 1.: Wenn Du ein Programm generell im Hintergrund startest, bekommst Du *sofort* die Befehlszeile der Konsole, sonst ist es nicht im Hintergrund gestartet.

Ansonsten ist die Frage ob Deine Threads denn alle Daemon-Threads sind oder sich selbst beenden. Denn sonst endet das Programm erst wenn alle Threads beendet sind, also sich selbst beendet haben.

Ad 2.: Geh mal jede einzelne Zeile durch und mach Dir klar was die bewirkt. Dann findest Du die Stelle die offensichtlich dafür sorgt, dass der Vorgang sich in der Konsole ”abspielt”.
Benutzeravatar
mobby
User
Beiträge: 76
Registriert: Donnerstag 17. April 2014, 09:43

@BlackJack: Das ist richtig, ich starte das Skript im Hintergrund und ich bin sofort wieder in der Befehlszeile. Ich glaube ich löse zuerst mein Email Problem. Dank deinem Hinweis gehe ich mal davon aus, dass folgende Zeilen für den "print" verantwortlich sind:

Code: Alles auswählen

server = smtplib.SMTP('', 25)
server.set_debuglevel(1)
server.ehlo()
server.starttls()
server.login('','')
server.sendmail(fromaddr, toaddr, text)
server.quit()
Nur was muss ich machen, damit keine Konsolenausgabe passiert?
BlackJack

@mobby: Was machen denn die einzelnen Zeilen. Du musst Dir bei jeder doch etwas gedacht haben.
Benutzeravatar
mobby
User
Beiträge: 76
Registriert: Donnerstag 17. April 2014, 09:43

@BlackJack: öh *hust* okay, schon etwas peinlich.

SMTP.set_debuglevel(level):
Set the debug output level. A true value for level results in debug messages for connection and for all messages sent to and received from the server.


Lasse ich das weg, gibt es auch keine Konsolenausgabe ;). Danke, dass du mir da die Augen geöffnet hast.
Und damit hat sich auch das Problem Nr. 1 gelöst, jetzt funktioniert alles mit dem Hintergrund-Prozess. :)
Benutzeravatar
mobby
User
Beiträge: 76
Registriert: Donnerstag 17. April 2014, 09:43

Das mit dem subprocess hat mich nicht losgelassen und ich habe mich nochmals damit beschäftigt. Ich hoffe, dass ich jetzt auf dem richtigen Weg bin, kann aber die Variable nicht übergeben:

Code: Alles auswählen

import os
import subprocess

pid = str(os.getpid())
subprocess.Popen(["/bin/kill", "-SIGTERM", pid])
Mir ist bewusst, dass es sich ab dem 2. Listenelement um Argumente des Programms "kill" handelt. Und als zweites Argument erwartet kill eben die PID, nur wie bekomme ich das umgesetzt, wenn ich die PID vorher in einer Variable habe?

Auch beim Ermitteln der PID bin ich weiter:

Code: Alles auswählen

import subprocess

pid = subprocess.check_output(["pidof", "-s", "python", "scriptname"])
Passt so oder?
BlackJack

@mobby: Woran erkennst Du, dass die Variable nicht übergeben wird?
Benutzeravatar
mobby
User
Beiträge: 76
Registriert: Donnerstag 17. April 2014, 09:43

Code: Alles auswählen

import subprocess

pid = subprocess.check_output(["pidof", "-s", "python", "check.py"])
print pid
subprocess.Popen(["/bin/kill", "-15", pid])
Gibt folgenden Fehler:

3099

kill: failed to parse argument: '3099
'
BlackJack

@mobby: Ah, da ermittelst Du die PID aber anders als im vorher gezeigten Code. Schau mal wo das schliessende ' bei der Ausgabe der PID in der Fehlermeldung steht, dann fällt Dir das Problem vielleicht auf. :-)
Benutzeravatar
mobby
User
Beiträge: 76
Registriert: Donnerstag 17. April 2014, 09:43

:mrgreen: ja ich dacht mir, wenn dann gleich richtig! Ähm, ja ich sehe da ist noch eine Leerzeile drin. Das ist mir auch beim print Befehl schon aufgefallen.

Code: Alles auswählen

import subprocess

pid = str.split(subprocess.check_output(["pidof", "-s", "python", "script.py"]))
print pid[0]
subprocess.Popen(["/bin/kill", "-15", pid[0]])
FUNKTIONIIEEEERRRRTT :) wuhuuu
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

@mobby: wenn Du eigentlich ein strip willst, solltest Du das nicht mit split nachbauen.

Nur zur Vollständigkeit: kill ist keine Lösung des Problems, sondern ein Herumdoktorn an den Symptomen (ala, ich hab Kopfweh, also Kopf ab). Lösung ist ein Signalmechanismus an den Hauptthread und geordnetes Beenden des Programms.
Benutzeravatar
mobby
User
Beiträge: 76
Registriert: Donnerstag 17. April 2014, 09:43

@Sirius3: Das mit dem strip würde dann so aussehen:

Code: Alles auswählen

import subprocess

pid= subprocess.check_output(["pidof", "-s", "python", "script.py"])
print pid.strip()
subprocess.Popen(["kill", "-15", pid.strip()])
Läuft auf jeden Fall.

Um die Vervollständigung aufzugreifen, was verstehst du denn unter "geordnetes Beenden des Programms"? Und wie kann ich das umsetzen? In meiner Vorstellung ist lässt sich irgendwie schlecht einordnen, wie ich aus dem Thread heraus etwas an den Hauptprozess zurückgeben kann, was dann dort eine "Reaktion" auslöst.
BlackJack

@mobby: Üblicherweise hat man im Hauptthread nur die Steuerung die mit den gestarteten Threads zum Beispiel über eine Queue kommuniziert. Wobei bei dem Beispiel das vielleicht noch nicht einmal nötig ist. Wenn der Thread der Werte in die Datenbank einträgt der einzige ist der das Programm beenden können soll, dann macht man diesen Teil einfach zu Hauptthread. Wenn es dann noch okay ist wenn die anderen Threads zum Programmende einfach so mitten drin ohne Aufräumarbeiten aufhören können, dann macht man die zu Daemon-Threads und lässt das Programm ganz einfach enden wenn das Eintragen in die DB nicht klappt.
Antworten