我写了一个Python脚本,检查一个特定的电子邮件地址,并将新的电子邮件传递给一个外部程序。如何让这个脚本全天候执行,比如在Linux中将其转换为守护进程或服务。我是否还需要一个在程序中永不结束的循环,或者可以通过多次重新执行代码来完成?


当前回答

如果你正在使用终端(ssh或其他东西),并且你想在退出终端后保持长时间的脚本工作,你可以尝试这样做:

屏幕

Apt-get安装屏幕

在里面创建一个虚拟终端(即abc): screen -dmS abc

现在我们连接到abc: screen -r abc

因此,现在我们可以运行python脚本:python keep_sending_mail .py

从现在开始,你可以直接关闭你的终端,但是,python脚本将继续运行而不是被关闭

因为这个keep_sending_mail .py的PID是虚拟屏幕的子进程,而不是 终端(ssh)

如果你想检查你的脚本运行状态,你可以再次使用screen -r abc

其他回答

你有两个选择。

Make a proper cron job that calls your script. Cron is a common name for a GNU/Linux daemon that periodically launches scripts according to a schedule you set. You add your script into a crontab or place a symlink to it into a special directory and the daemon handles the job of launching it in the background. You can read more at Wikipedia. There is a variety of different cron daemons, but your GNU/Linux system should have it already installed. Use some kind of python approach (a library, for example) for your script to be able to daemonize itself. Yes, it will require a simple event loop (where your events are timer triggering, possibly, provided by sleep function).

我不建议你选择2。,因为您实际上是在重复cron功能。Linux系统范例是让多个简单工具交互并解决您的问题。除非有其他原因需要创建守护进程(除了定期触发之外),否则选择其他方法。

此外,如果你使用daemonize进行循环并且发生了崩溃,那么没有人会在那之后检查邮件(正如Ivan Nevostruev在回答的评论中指出的那样)。而如果脚本作为cron作业添加,它将再次触发。

假设您真的希望您的循环作为后台服务全天候运行

对于一个不需要向代码中注入库的解决方案,你可以简单地创建一个服务模板,因为你使用的是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/)中,*. .使用以下systemctl命令安装它(可能需要sudo权限):

systemctl enable <service file name without .service extension>

systemctl daemon-reload

systemctl start <service file name without .service extension>

然后你可以使用命令检查你的服务是否在运行:

systemctl | grep running

使用你的系统提供的任何服务管理器——例如在Ubuntu下使用upstart。这将为您处理所有细节,如启动时启动,崩溃时重新启动等。

您可以在脚本中或像这样在另一个脚本中作为子进程运行进程

subprocess.Popen(arguments, close_fds=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL)

或者使用现成的工具

https://github.com/megashchik/d-handler

我推荐这个解决方案。您需要继承和重写方法运行。

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()