web-dev-qa-db-de.com

So erstellen Sie ein Python - Skript, das unter Linux wie ein Dienst oder ein Daemon ausgeführt wird

Ich habe ein Python Skript geschrieben, das eine bestimmte E-Mail-Adresse überprüft und neue E-Mails an ein externes Programm weiterleitet. Wie kann ich erreichen, dass dieses Skript rund um die Uhr ausgeführt wird, z. B. als Daemon oder als Dienst unter Linux? Benötige ich auch eine Schleife, die niemals im Programm endet, oder kann dies einfach dadurch geschehen, dass der Code mehrmals ausgeführt wird?

159
adhanlon

Hier haben Sie zwei Möglichkeiten.

  1. Erstellen Sie einen richtigen Cron-Job , der Ihr Skript aufruft. Cron ist ein gebräuchlicher Name für einen GNU/Linux-Daemon, der Skripte regelmäßig nach einem von Ihnen festgelegten Zeitplan startet. Sie fügen Ihr Skript einer Crontab hinzu oder platzieren einen Symlink dazu in einem speziellen Verzeichnis, und der Dämon übernimmt den Start im Hintergrund. Sie können lesen Sie mehr bei Wikipedia. Es gibt verschiedene Cron-Daemons, aber auf Ihrem GNU/Linux-System sollte es bereits installiert sein.

  2. Verwenden Sie eine Art Python-Ansatz (z. B. eine Bibliothek), damit sich Ihr Skript selbst dämonisieren kann. Ja, es ist eine einfache Ereignisschleife erforderlich (bei der Ihre Ereignisse durch einen Timer ausgelöst werden, möglicherweise durch die Sleep-Funktion).

Ich würde Ihnen nicht empfehlen, 2. zu wählen, da Sie in der Tat die Cron-Funktionalität wiederholen würden. Das Linux-System-Paradigma besteht darin, mehrere einfache Tools interagieren zu lassen und Ihre Probleme zu lösen. Wählen Sie den anderen Ansatz, es sei denn, es gibt zusätzliche Gründe, warum Sie einen Daemon erstellen sollten (zusätzlich zum regelmäßigen Auslösen).

Wenn Sie daemonize mit einer Schleife verwenden und ein Absturz auftritt, wird die E-Mail danach von niemandem geprüft (wie von Ivan Nevostruev in den Kommentaren zu this answer). Wenn das Skript als Cron-Job hinzugefügt wird, wird es nur erneut ausgelöst.

92
P Shved

Hier ist eine Nice-Klasse, die aus hier stammt:

#!/usr/bin/env python

import sys, os, time, atexit
from signal import SIGTERM

class Daemon:
        """
        A generic daemon class.

        Usage: subclass the Daemon class and override the run() method
        """
        def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
                self.stdin = stdin
                self.stdout = stdout
                self.stderr = stderr
                self.pidfile = pidfile

        def daemonize(self):
                """
                do the UNIX double-fork magic, see Stevens' "Advanced
                Programming in the UNIX Environment" for details (ISBN 0201563177)
                http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
                """
                try:
                        pid = os.fork()
                        if pid > 0:
                                # exit first parent
                                sys.exit(0)
                except OSError, e:
                        sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror))
                        sys.exit(1)

                # decouple from parent environment
                os.chdir("/")
                os.setsid()
                os.umask(0)

                # do second fork
                try:
                        pid = os.fork()
                        if pid > 0:
                                # exit from second parent
                                sys.exit(0)
                except OSError, e:
                        sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror))
                        sys.exit(1)

                # redirect standard file descriptors
                sys.stdout.flush()
                sys.stderr.flush()
                si = file(self.stdin, 'r')
                so = file(self.stdout, 'a+')
                se = file(self.stderr, 'a+', 0)
                os.dup2(si.fileno(), sys.stdin.fileno())
                os.dup2(so.fileno(), sys.stdout.fileno())
                os.dup2(se.fileno(), sys.stderr.fileno())

                # write pidfile
                atexit.register(self.delpid)
                pid = str(os.getpid())
                file(self.pidfile,'w+').write("%s\n" % pid)

        def delpid(self):
                os.remove(self.pidfile)

        def start(self):
                """
                Start the daemon
                """
                # Check for a pidfile to see if the daemon already runs
                try:
                        pf = file(self.pidfile,'r')
                        pid = int(pf.read().strip())
                        pf.close()
                except IOError:
                        pid = None

                if pid:
                        message = "pidfile %s already exist. Daemon already running?\n"
                        sys.stderr.write(message % self.pidfile)
                        sys.exit(1)

                # Start the daemon
                self.daemonize()
                self.run()

        def stop(self):
                """
                Stop the daemon
                """
                # Get the pid from the pidfile
                try:
                        pf = file(self.pidfile,'r')
                        pid = int(pf.read().strip())
                        pf.close()
                except IOError:
                        pid = None

                if not pid:
                        message = "pidfile %s does not exist. Daemon not running?\n"
                        sys.stderr.write(message % self.pidfile)
                        return # not an error in a restart

                # Try killing the daemon process       
                try:
                        while 1:
                                os.kill(pid, SIGTERM)
                                time.sleep(0.1)
                except OSError, err:
                        err = str(err)
                        if err.find("No such process") > 0:
                                if os.path.exists(self.pidfile):
                                        os.remove(self.pidfile)
                        else:
                                print str(err)
                                sys.exit(1)

        def restart(self):
                """
                Restart the daemon
                """
                self.stop()
                self.start()

        def run(self):
                """
                You should override this method when you subclass Daemon. It will be called after the process has been
                daemonized by start() or restart().
                """
70
the_drow

Sie sollten die Python-Daemon Bibliothek benutzen, sie kümmert sich um alles.

Aus PyPI: Bibliothek zum Implementieren eines anständigen Unix-Daemon-Prozesses.

54
Prody

Sie können fork () verwenden, um Ihr Skript vom tty zu trennen und weiter ausführen zu lassen.

import os, sys
fpid = os.fork()
if fpid!=0:
  # Running as daemon now. PID is fpid
  sys.exit(0)

Natürlich müssen Sie auch eine Endlosschleife implementieren, z

while 1:
  do_your_check()
  sleep(5)

Hoffe, Sie haben damit angefangen.

38
jhwist

Sie können das Skript python als Dienst ausführen lassen, indem Sie ein Shell-Skript verwenden. Erstellen Sie zunächst ein Shell-Skript, um das Skript python auszuführen (scriptname beliebiger Name )

#!/bin/sh
script='/home/.. full path to script'
/usr/bin/python $script &

erstellen Sie jetzt eine Datei in /etc/init.d/scriptname

#! /bin/sh

PATH=/bin:/usr/bin:/sbin:/usr/sbin
DAEMON=/home/.. path to Shell script scriptname created to run python script
PIDFILE=/var/run/scriptname.pid

test -x $DAEMON || exit 0

. /lib/lsb/init-functions

case "$1" in
  start)
     log_daemon_msg "Starting feedparser"
     start_daemon -p $PIDFILE $DAEMON
     log_end_msg $?
   ;;
  stop)
     log_daemon_msg "Stopping feedparser"
     killproc -p $PIDFILE $DAEMON
     PID=`ps x |grep feed | head -1 | awk '{print $1}'`
     kill -9 $PID       
     log_end_msg $?
   ;;
  force-reload|restart)
     $0 stop
     $0 start
   ;;
  status)
     status_of_proc -p $PIDFILE $DAEMON atd && exit 0 || exit $?
   ;;
 *)
   echo "Usage: /etc/init.d/atd {start|stop|restart|force-reload|status}"
   exit 1
  ;;
esac

exit 0

Jetzt können Sie Ihr python Skript mit dem Befehl /etc/init.d/scriptname starten oder stoppen.

15
Kishore K

cron ist eindeutig eine gute Wahl für viele Zwecke. Es wird jedoch kein Dienst oder Dämon erstellt, wie Sie es im OP angefordert haben. cron führt Jobs nur in regelmäßigen Abständen (dh der Job startet und stoppt) und nicht öfter als einmal pro Minute aus. Es gibt Probleme mit cron. Wenn beispielsweise eine frühere Instanz Ihres Skripts beim nächsten Start des Zeitplans für cron noch ausgeführt wird und eine neue Instanz gestartet wird, ist das in Ordnung? cron verarbeitet keine Abhängigkeiten. Es wird nur versucht, einen Job zu starten, wenn der Zeitplan dies vorsieht.

Wenn Sie eine Situation finden, in der Sie wirklich einen Daemon benötigen (einen Prozess, der nie aufhört zu laufen), schauen Sie sich supervisord an. Es bietet eine einfache Möglichkeit, ein normales, nicht dämonisiertes Skript oder Programm zu verpacken und es wie einen Dämon zu betreiben. Dies ist ein viel besserer Weg, als einen nativen Python Daemon zu erstellen.

12
Chris Johnson

Ein einfaches und unterstütztes version ist Daemonize.

Installieren Sie es von Python Package Index (PyPI):

$ pip install daemonize

und dann wie folgt verwenden:

...
import os, sys
from daemonize import Daemonize
...
def main()
      # your code here

if __== '__main__':
        myname=os.path.basename(sys.argv[0])
        pidfile='/tmp/%s' % myname       # any name
        daemon = Daemonize(app=myname,pid=pidfile, action=main)
        daemon.start()
10
fcm

wie wäre es mit $Nohup Befehl unter Linux?

Ich verwende es, um meine Befehle auf meinem Bluehost-Server auszuführen.

Bitte raten Sie mir, wenn ich falsch liege.

8
faisal00813

Wenn Sie Terminal (ssh oder so) verwenden und ein Langzeitskript nach dem Abmelden vom Terminal weiterarbeiten möchten, können Sie Folgendes versuchen:

screen

apt-get install screen

erstelle ein virtuelles Terminal (nämlich abc): screen -dmS abc

jetzt verbinden wir uns mit abc: screen -r abc

Nun können wir python script: python Keep_sending_mail.py

von nun an können Sie Ihr Terminal direkt schließen. Das Skript python wird jedoch weiter ausgeführt, anstatt heruntergefahren zu werden

Da die PID dieses Keep_sending_mail.py Zum virtuellen Bildschirm und nicht zum Terminal (ssh) gehört

Wenn Sie den Status Ihres Skripts überprüfen möchten, können Sie erneut screen -r abc Verwenden

4
Microos

Lesen Sie sich zuerst die Mail-Aliase durch. Ein Mail-Alias ​​erledigt dies im Mail-System, ohne dass Sie mit Daemons, Diensten oder Ähnlichem herumalbern müssen.

Sie können ein einfaches Skript schreiben, das von sendmail jedes Mal ausgeführt wird, wenn eine E-Mail-Nachricht an ein bestimmtes Postfach gesendet wird.

Siehe http://www.feep.net/sendmail/tutorial/intro/aliases.html

Wenn Sie wirklich einen unnötig komplexen Server schreiben möchten, können Sie dies tun.

Nohup python myscript.py &

Das ist alles was es braucht. Ihr Skript wird einfach wiederholt und in den Ruhezustand versetzt.

import time
def do_the_work():
    # one round of polling -- checking email, whatever.
while True:
    time.sleep( 600 ) # 10 min.
    try:
        do_the_work()
    except:
        pass
3
S.Lott

Verwenden Sie den Service Manager, den Ihr System anbietet - zum Beispiel unter Ubuntu use pstart. Dadurch werden alle Details für Sie verarbeitet, z. B. Start beim Booten, Neustart beim Absturz usw.

1
Richard

Ich würde diese Lösung empfehlen. Sie müssen die Methode run erben und überschreiben.

import sys
import os
from signal import SIGTERM
from abc import ABCMeta, abstractmethod



class Daemon(object):
    __metaclass__ = ABCMeta


    def __init__(self, pidfile):
        self._pidfile = pidfile


    @abstractmethod
    def run(self):
        pass


    def _daemonize(self):
        # decouple threads
        pid = os.fork()

        # stop first thread
        if pid > 0:
            sys.exit(0)

        # write pid into a pidfile
        with open(self._pidfile, 'w') as f:
            print >> f, os.getpid()


    def start(self):
        # if daemon is started throw an error
        if os.path.exists(self._pidfile):
            raise Exception("Daemon is already started")

        # create and switch to daemon thread
        self._daemonize()

        # run the body of the daemon
        self.run()


    def stop(self):
        # check the pidfile existing
        if os.path.exists(self._pidfile):
            # read pid from the file
            with open(self._pidfile, 'r') as f:
                pid = int(f.read().strip())

            # remove the pidfile
            os.remove(self._pidfile)

            # kill daemon
            os.kill(pid, SIGTERM)

        else:
            raise Exception("Daemon is not started")


    def restart(self):
        self.stop()
        self.start()
1
Fomalhaut

um etwas zu erstellen, das wie ein Dienst läuft, können Sie folgendes verwenden:

Als Erstes müssen Sie das Cement Framework installieren: Cement Frame Work ist ein CLI-Framework, auf dem Sie Ihre Anwendung bereitstellen können.

kommandozeilenoberfläche der App:

interface.py

 from cement.core.foundation import CementApp
 from cement.core.controller import CementBaseController, expose
 from YourApp import yourApp

 class Meta:
    label = 'base'
    description = "your application description"
    arguments = [
        (['-r' , '--run'],
          dict(action='store_true', help='Run your application')),
        (['-v', '--version'],
          dict(action='version', version="Your app version")),
        ]
        (['-s', '--stop'],
          dict(action='store_true', help="Stop your application")),
        ]

    @expose(hide=True)
    def default(self):
        if self.app.pargs.run:
            #Start to running the your app from there !
            YourApp.yourApp()
        if self.app.pargs.stop:
            #Stop your application
            YourApp.yourApp.stop()

 class App(CementApp):
       class Meta:
       label = 'Uptime'
       base_controller = 'base'
       handlers = [MyBaseController]

 with App() as app:
       app.run()

YourApp.py Klasse:

 import threading

 class yourApp:
     def __init__:
        self.loger = log_exception.exception_loger()
        thread = threading.Thread(target=self.start, args=())
        thread.daemon = True
        thread.start()

     def start(self):
        #Do every thing you want
        pass
     def stop(self):
        #Do some things to stop your application

Denken Sie daran, dass Ihre App auf einem Thread ausgeführt werden muss, um ein Daemon zu sein

Um die App auszuführen, führen Sie dies einfach in der Befehlszeile aus

python interface.py --hilfe

0