208

特定の電子メール アドレスをチェックし、新しい電子メールを外部プログラムに渡す Python スクリプトを作成しました。このスクリプトを 24 時間年中無休で実行するにはどうすればよいですか (Linux でデーモンまたはサービスに変換するなど)。プログラム内で決して終わらないループも必要ですか、それともコードを複数回再実行するだけで実行できますか?

4

15 に答える 15

105

ここには 2 つのオプションがあります。

  1. スクリプトを呼び出す適切なcron ジョブを作成します。Cron は、設定したスケジュールに従って定期的にスクリプトを起動する GNU/Linux デーモンの一般名です。スクリプトを crontab に追加するか、スクリプトへのシンボリック リンクを特別なディレクトリに配置すると、デーモンがスクリプトをバックグラウンドで起動するジョブを処理します。ウィキペディアで詳細を読むことができます。さまざまな cron デーモンがありますが、GNU/Linux システムには既にインストールされている必要があります。

  2. スクリプトがそれ自体をデーモン化できるようにするには、ある種のPython アプローチ(ライブラリなど) を使用します。はい、単純なイベント ループが必要です (イベントがタイマー トリガーであり、スリープ機能によって提供される可能性があります)。

2. を選択することはお勧めしません。実際には、cron 機能を繰り返すことになるからです。Linux システムのパラダイムは、複数の単純なツールが相互作用して問題を解決できるようにすることです。(定期的にトリガーすることに加えて) デーモンを作成する必要がある追加の理由がない限り、他のアプローチを選択してください。

また、ループでデーモン化を使用してクラッシュが発生した場合、その後は誰もメールをチェックしません (この回答へのコメントでIvan Nevostruevが指摘したように)。スクリプトが cron ジョブとして追加された場合、スクリプトは再びトリガーされます。

于 2009-10-21T19:43:06.880 に答える
76

ここから取った素敵なクラスがあります

#!/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().
                """
于 2011-06-16T15:58:40.243 に答える
62

python-daemonライブラリを使用する必要があります。これがすべてを処理します。

PyPI から:行儀の良い Unix デーモン プロセスを実装するためのライブラリ。

于 2009-10-21T19:43:45.630 に答える
41

fork() を使用してスクリプトを tty から切り離し、次のように実行し続けることができます。

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

もちろん、次のような無限ループも実装する必要があります

while 1:
  do_your_check()
  sleep(5)

これで始められることを願っています。

于 2009-10-21T19:45:12.023 に答える
35

ループをバックグラウンド サービスとして 24 時間 365 日実行することが本当に必要であると仮定すると、

コードにライブラリを挿入する必要のないソリューションの場合、Linux を使用しているため、単純にサービス テンプレートを作成できます。

[Unit]
Description = <Your service description here>
After = network.target # Assuming you want to start after network interfaces are made available
 
[Service]
Type = simple
ExecStart = python <Path of the script you want to run>
User = # User to run the script as
Group = # Group to run the script as
Restart = on-failure # Restart when there are errors
SyslogIdentifier = <Name of logs for the service>
RestartSec = 5
TimeoutStartSec = infinity
 
[Install]
WantedBy = multi-user.target # Make it accessible to other users

そのファイルをデーモン サービス フォルダー (通常は/etc/systemd/system/) 内の*.serviceファイルに配置し、次の systemctl コマンドを使用してインストールします (sudo 権限が必要になる可能性があります)。

systemctl enable <service file name without .service extension>

systemctl daemon-reload

systemctl start <service file name without .service extension>

次に、次のコマンドを使用して、サービスが実行されていることを確認できます。

systemctl | grep running
于 2020-06-29T17:37:35.250 に答える
16

シェル スクリプトを使用して、Python スクリプトをサービスとして実行することもできます。まずこのようにpythonスクリプトを実行するシェルスクリプトを作成します(スクリプト名は任意の名前)

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

/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

これで、コマンド /etc/init.d/scriptname start または stop を使用して、Python スクリプトを開始および停止できます。

于 2013-10-22T09:56:08.620 に答える
15

シンプルでサポートされているバージョンDaemonize.

Python Package Index (PyPI) からインストールします。

$ pip install daemonize

そして次のように使用します:

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

if __name__ == '__main__':
        myname=os.path.basename(sys.argv[0])
        pidfile='/tmp/%s' % myname       # any name
        daemon = Daemonize(app=myname,pid=pidfile, action=main)
        daemon.start()
于 2016-04-03T11:08:46.620 に答える
12

cron多くの目的で優れた選択肢であることは明らかです。ただし、OPで要求したとおりにサービスまたはデーモンを作成しません。 cronジョブを定期的に実行するだけで (つまり、ジョブの開始と停止を意味します)、頻度は 1 分に 1 回までです。問題がありますcron。たとえば、スクリプトの前のインスタンスがまだ実行されていて、次回cronスケジュールが来て新しいインスタンスが起動された場合、それは問題ありませんか? cron依存関係を処理しません。スケジュールが指示したときにジョブを開始しようとするだけです。

本当にデーモン (実行を停止しないプロセス) が必要な状況を見つけた場合は、 を参照してくださいsupervisord。通常のデーモン化されていないスクリプトまたはプログラムをラップし、デーモンのように動作させる簡単な方法を提供します。これは、ネイティブの Python デーモンを作成するよりもはるかに優れた方法です。

于 2013-10-22T10:36:53.417 に答える
11

$nohupLinuxでコマンドを使用するのはどうですか?

Bluehost サーバーでコマンドを実行するために使用します。

間違っていたらアドバイスお願いします。

于 2012-01-21T21:00:02.227 に答える
4

まず、メール エイリアスについて調べます。メールエイリアスは、デーモンやサービスなどをいじる必要なく、メールシステム内でこれを行います。

特定のメールボックスにメール メッセージが送信されるたびに sendmail によって実行される簡単なスクリプトを作成できます。

http://www.feep.net/sendmail/tutorial/intro/aliases.htmlを参照してください。

不必要に複雑なサーバーを本当に書きたい場合は、これを行うことができます。

nohup python myscript.py &

それだけです。スクリプトは単純にループしてスリープします。

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
于 2009-10-21T19:44:22.447 に答える
3

このソリューションをお勧めします。method を継承してオーバーライドする必要があります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()
于 2015-05-08T11:12:15.563 に答える
2

サービスのように実行されているものを作成するには、次のことを使用できます。

最初に行う必要があるのは、Cementフレームワークのインストールです。Cement フレームワークは、アプリケーションをデプロイできる CLI フレームワークです。

アプリのコマンド ライン インターフェイス:

インターフェイス.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 クラス:

 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

デーモンになるには、アプリをスレッドで実行する必要があることに注意してください

アプリを実行するには、コマンドラインでこれを行うだけです

python interface.py --help

于 2017-08-07T09:52:23.507 に答える
1

システムが提供するサービス マネージャーを使用します。たとえば、Ubuntu ではupstartを使用します。これにより、起動時の開始、クラッシュ時の再起動など、すべての詳細が処理されます。

于 2014-01-03T16:36:35.703 に答える