如何使Python脚本像Linux中的服务或守护程序一样运行

adh*_*lon 163 python linux scripting daemons

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

P S*_*ved 93

你有两个选择.

  1. 做一个调用脚本的正确的cron作业.Cron是GNU/Linux守护程序的通用名称,它根据您设置的计划定期启动脚本.将脚本添加到crontab中或将符号链接放入特殊目录中,守护程序处理在后台启动它的作业.您可以在维基百科阅读更多内容.有各种不同的cron守护进程,但你的GNU/Linux系统应该已经安装了它.

  2. 使用某种python方法(例如一个库)让你的脚本能够守护自己.是的,它需要一个简单的事件循环(你的事件是定时器触发,可能由睡眠功能提供).

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

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

  • +1到cronjob.我不认为该问题指出它正在检查本地邮件帐户,因此邮件过滤器不适用 (6认同)

the*_*row 71

这是一个很好的课程,取自这里:

#!/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().
                """
Run Code Online (Sandbox Code Playgroud)

  • 系统重启后是否会自动重启?因为当系统重新启动时,该进程将被杀死,对吗? (2认同)

Pro*_*ody 57

你应该使用python-daemon库,它会处理所有事情.

从PyPI:Library实现一个表现良好的Unix守护进程.

  • Ditto Jorge Vargas的评论.在查看代码之后,它实际上看起来是一段很好的代码,但是完全没有文档和示例使得它很难使用,这意味着大多数开发人员都会理所当然地忽略它以获得更好的文档替代方案. (2认同)

jhw*_*ist 38

您可以使用fork()从tty中分离脚本并让它继续运行,如下所示:

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

当然你还需要实现无限循环,比如

while 1:
  do_your_check()
  sleep(5)
Run Code Online (Sandbox Code Playgroud)

希望这是你的开始.


Hei*_*tro 22

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

对于不涉及使用库注入代码的解决方案,您可以简单地创建一个服务模板,因为您使用的是 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
Run Code Online (Sandbox Code Playgroud)

将该文件放在您的守护程序服务文件夹(通常是/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>
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用以下命令检查您的服务是否正在运行:

systemctl | grep running
Run Code Online (Sandbox Code Playgroud)

  • @PouJa 请发布一个包含详细信息的新问题。 (2认同)

Kis*_*e K 14

您还可以使用shell脚本将python脚本作为服务运行.首先创建一个shell脚本来运行这样的python脚本(scriptname仲裁名称)

#!/bin/sh
script='/home/.. full path to script'
/usr/bin/python $script &
Run Code Online (Sandbox Code Playgroud)

现在在/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
Run Code Online (Sandbox Code Playgroud)

现在,您可以使用命令/etc/init.d/scriptname start或stop启动和停止python脚本.


Chr*_*son 12

cron显然是很多目的的绝佳选择.但是,它不会像您在OP中请求的那样创建服务或守护程序. cron只是定期运行工作(意味着工作开始和停止),并且不会超过一次/分钟.存在一些问题cron- 例如,如果您的脚本的先前实例在下次cron计划到来并且启动新实例时仍在运行,那可以吗? cron不处理依赖; 它只是试图在时间表说到的时候开始工作.

如果您发现一个真正需要守护进程的情况(一个永不停止运行的进程),请查看supervisord.它提供了一种简单的方法来包装正常的非守护程序脚本或程序,并使其像守护程序一样运行.这是一种比创建本机Python守护进程更好的方法.


fcm*_*fcm 10

一个简单且受支持的版本是Deamonize从Python Package Index(PyPI)安装它:

$ pip install daemonize
Run Code Online (Sandbox Code Playgroud)

然后使用像:

...
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()
Run Code Online (Sandbox Code Playgroud)

  • 系统重启后是否会自动重启?因为当系统重新启动时,该进程将被杀死,对吗? (2认同)

fai*_*813 8

如何$nohup在linux上使用命令?

我使用它在我的Bluehost服务器上运行我的命令.

如果我错了,请指教.


Mic*_*oos 7

如果您正在使用终端(ssh 或其他东西),并且希望在从终端注销后保持脚本长时间工作,您可以尝试以下操作:

screen

apt-get install screen

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

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

所以,现在我们可以运行 python 脚本了: python keep_sending_mails.py

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

由于 thiskeep_sending_mails.py的 PID 是虚拟屏幕的子进程而不是终端(ssh)

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

  • 虽然这有效,但它非常快速和肮脏,应该在生产中避免 (2认同)

Gog*_*are 5

Ubuntu 有一种非常简单的方法来管理服务。对于python,不同之处在于所有依赖项(包)必须位于运行主文件的同一目录中。

我只是设法创建这样的服务来向我的客户提供天气信息。脚步:

  • 像往常一样创建您的 Python 应用程序项目。

  • 在本地安装所有依赖项,例如: sudo pip3 install package_name -t 。

  • 创建命令行变量并在代码中处理它们(如果需要)

  • 创建服务文件。一些(极简主义)像:

      [Unit]
      Description=1Droid Weather meddleware provider
    
      [Service]
      Restart=always
      User=root
      WorkingDirectory=/home/ubuntu/weather
      ExecStart=/usr/bin/python3 /home/ubuntu/weather/main.py httpport=9570  provider=OWMap
    
      [Install]
      WantedBy=multi-user.target
    
    Run Code Online (Sandbox Code Playgroud)
  • 将文件另存为 myweather.service(例如)

  • 如果在当前目录中启动,请确保您的应用程序运行

      python3  main.py httpport=9570  provider=OWMap
    
    Run Code Online (Sandbox Code Playgroud)
  • 上面生成的名为 myweather.service 的服务文件(扩展名为 .service 很重要)将被系统视为您的服务的名称。这是您将用于与服务交互的名称。

  • 复制服务文件:

      sudo cp myweather.service /lib/systemd/system/myweather.service
    
    Run Code Online (Sandbox Code Playgroud)
  • 刷新恶魔注册表:

      sudo systemctl daemon-reload
    
    Run Code Online (Sandbox Code Playgroud)
  • 停止服务(如果它正在运行)

      sudo service myweatherr stop
    
    Run Code Online (Sandbox Code Playgroud)
  • 启动服务:

      sudo service myweather start
    
    Run Code Online (Sandbox Code Playgroud)
  • 检查状态(带有打印语句所在位置的日志文件):

      tail -f /var/log/syslog
    
    Run Code Online (Sandbox Code Playgroud)
  • 或检查状态:

      sudo service myweather status
    
    Run Code Online (Sandbox Code Playgroud)
  • 如果需要,重新开始进行另一次迭代

此服务现在正在运行,即使您注销也不会受到影响。如果主机关闭并重新启动,则是,此服务将重新启动...我的移动 android 应用程序的信息...