J'ai écrit un script Python qui vérifie une adresse électronique donnée et transmet les nouveaux courriers électroniques à un programme externe. Comment puis-je exécuter ce script 24 heures sur 24 et 7 jours sur 7, par exemple en le transformant en démon ou en service sous Linux. Aurais-je également besoin d'une boucle qui ne finisse jamais dans le programme, ou cela peut-il être fait simplement en ré-exécutant le code plusieurs fois?
Vous avez deux options ici.
Créez un travail cron approprié qui appelle votre script. Cron est un nom commun pour un démon GNU/Linux qui lance périodiquement des scripts selon une planification que vous avez définie. Vous ajoutez votre script dans une crontab ou placez un lien symbolique vers celle-ci dans un répertoire spécial et le démon se charge de le lancer en arrière-plan. Vous pouvez en savoir plus sur Wikipedia. Il existe une variété de démons cron différents, mais votre système GNU/Linux devrait déjà l'avoir déjà installé.
Utilisez une sorte d’approche python (une bibliothèque, par exemple) pour que votre script puisse se démoniser. Oui, il faudra une simple boucle d’événement (où vos événements déclenchent le minuteur, éventuellement, fournis par la fonction de veille).
Je ne vous recommanderais pas de choisir 2., car vous répéteriez en fait la fonctionnalité cron. Le paradigme du système Linux consiste à laisser plusieurs outils simples interagir et résoudre vos problèmes. À moins qu'il y ait des raisons supplémentaires pour lesquelles vous devriez créer un démon (en plus du déclenchement périodique), choisissez une autre approche.
De plus, si vous utilisez daemonize avec une boucle et qu'un crash se produit, personne ne vérifiera le courrier après cela (comme indiqué par Ivan Nevostruev dans les commentaires de this answer). Tandis que si le script est ajouté en tant que tâche cron, il se déclenchera à nouveau.
Voici une classe de Nice tirée de ici :
#!/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().
"""
Vous devriez utiliser la bibliothèque python-daemon , elle s’occupe de tout.
De PyPI: Bibliothèque pour implémenter un processus démon Unix bien conçu.
Vous pouvez utiliser fork () pour détacher votre script du tty et le laisser continuer à s'exécuter, comme suit:
import os, sys
fpid = os.fork()
if fpid!=0:
# Running as daemon now. PID is fpid
sys.exit(0)
Bien sûr, vous devez également implémenter une boucle sans fin, comme
while 1:
do_your_check()
sleep(5)
J'espère que ça commence.
Vous pouvez également exécuter le script python en tant que service à l'aide d'un script Shell. Commencez par créer un script Shell pour exécuter le script python comme ceci (nom arbitraire du nom du script).
#!/bin/sh
script='/home/.. full path to script'
/usr/bin/python $script &
maintenant créez un fichier dans /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
Vous pouvez maintenant démarrer et arrêter votre script python à l'aide de la commande /etc/init.d/scriptname start or stop.
cron
est clairement un excellent choix pour de nombreuses raisons. Cependant, il ne crée pas de service ou de démon comme vous l'avez demandé dans l'OP. cron
exécute uniquement les travaux périodiquement (c'est-à-dire que le travail commence et s'arrête), et pas plus d'une fois/minute. Il y a des problèmes avec cron
- par exemple, si une instance précédente de votre script est toujours en cours d'exécution la prochaine fois que la planification cron
entre et lance une nouvelle instance, est-ce correct? cron
ne gère pas les dépendances; il essaie juste de commencer un travail quand l'horaire le dit.
Si vous trouvez une situation dans laquelle vous avez vraiment besoin d'un démon (un processus qui ne s'arrête jamais de s'exécuter), consultez supervisord
. Il fournit un moyen simple d'encapsuler un script ou un programme normal non démonisé et de le faire fonctionner comme un démon. C'est un bien meilleur moyen que de créer un démon natif Python.
Un simple et supporté version est Daemonize
.
Installez-le à partir de Python Index de paquet (PyPI):
$ pip install daemonize
et ensuite utiliser comme:
...
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()
comment utiliser $Nohup
commande sur linux?
Je l'utilise pour exécuter mes commandes sur mon serveur Bluehost.
S'il vous plaît des conseils si je me trompe.
Si vous utilisez un terminal (ssh ou autre chose) et que vous voulez conserver un script de longue durée après que vous vous êtes déconnecté du terminal, vous pouvez essayer ceci:
screen
apt-get install screen
créer un terminal virtuel à l'intérieur (à savoir abc): screen -dmS abc
maintenant on se connecte à abc: screen -r abc
Donc, maintenant nous pouvons exécuter python script: python Keep_sending_mail.py
à partir de maintenant, vous pouvez directement fermer votre terminal, cependant, le script python continuera à s'exécuter plutôt que d'être arrêté
Puisque ce PID de
Keep_sending_mail.py
appartient à l’écran virtuel et non au terminal (ssh)
Si vous souhaitez revenir en arrière pour vérifier l'état d'exécution du script, vous pouvez utiliser à nouveau screen -r abc
.
Commencez par lire les alias de messagerie. Un alias de messagerie le fera dans le système de messagerie sans que vous ayez à vous occuper de démons, de services ou quoi que ce soit du genre.
Vous pouvez écrire un script simple qui sera exécuté par sendmail chaque fois qu'un message est envoyé à une boîte aux lettres spécifique.
Voir http://www.feep.net/sendmail/tutorial/intro/aliases.html
Si vous voulez vraiment écrire un serveur inutilement complexe, vous pouvez le faire.
Nohup python myscript.py &
C'est tout ce qu'il faut. Votre script boucle et dort tout simplement.
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
Utilisez le gestionnaire de services proposé par votre système - par exemple sous Ubuntu, utilisez version récente. Ceci gérera tous les détails pour vous, tels que démarrer au démarrage, redémarrer en cas de crash, etc.
Je recommanderais cette solution. Vous devez hériter et remplacer la méthode run
.
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()
pour créer quelque chose qui fonctionne comme un service, vous pouvez utiliser cette chose:
La première chose que vous devez faire est d’installer le framework Cement : Le travail sur les cadres de ciment est un travail de cadre CLI sur lequel vous pouvez déployer votre application.
interface de ligne de commande de l'application:
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()
Votre classe App.py:
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
Gardez à l'esprit que votre application doit être exécutée sur un fil pour être démon.
Pour exécuter l'application, faites ceci en ligne de commande
python interface.py --help