bash 制作shell脚本守护进程的最佳方法?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3430330/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 19:33:34  来源:igfitidea点击:

Best way to make a shell script daemon?

bashshelldaemon

提问by Shawn J. Goff

I'm wondering if there is a better way to make a daemon that waits for something using only sh than:

我想知道是否有更好的方法来制作一个只使用 sh 等待某些东西的守护进程,而不是:

#! /bin/sh
trap processUserSig SIGUSR1
processUserSig() {
  echo "doing stuff"
}

while true; do
  sleep 1000
done

In particular, I'm wondering if there's any way to get rid of the loop and still have the thing listen for the signals.

特别是,我想知道是否有任何方法可以摆脱循环并仍然让事物监听信号。

采纳答案by Paused until further notice.

Use your system's daemon facility, such as start-stop-daemon.

使用系统的守护程序工具,例如start-stop-daemon

Otherwise, yes, there has to be a loop somewhere.

否则,是的,某处必须有一个循环。

回答by Ken B

Just backgrounding your script (./myscript &) will not daemonize it. See http://www.faqs.org/faqs/unix-faq/programmer/faq/, section 1.7, which describes what's necessary to become a daemon. You must disconnect it from the terminal so that SIGHUPdoes not kill it. You can take a shortcut to make a script appear to act like a daemon;

只是./myscript &将您的脚本 ( ) 放在后台不会对其进行守护进程。请参阅http://www.faqs.org/faqs/unix-faq/programmer/faq/,第 1.7 节,其中描述了成为守护进程的必要条件。您必须将其与终端断开连接,以免SIGHUP杀死它。您可以通过快捷方式使脚本看起来像守护进程;

nohup ./myscript 0<&- &>/dev/null &

will do the job. Or, to capture both stderr and stdout to a file:

会做的工作。或者,将 stderr 和 stdout 都捕获到文件中:

nohup ./myscript 0<&- &> my.admin.log.file &

However, there may be further important aspects that you need to consider. For example:

但是,您可能还需要考虑其他重要方面。例如:

  • You will still have a file descriptor open to the script, which means that the directory it's mounted in would be unmountable. To be a true daemon you should chdir("/")(or cd /inside your script), and fork so that the parent exits, and thus the original descriptor is closed.
  • Perhaps run umask 0. You may not want to depend on the umask of the caller of the daemon.
  • 您仍然会向脚本打开一个文件描述符,这意味着它挂载的目录将是不可挂载的。要成为一个真正的守护进程,您应该chdir("/")(或cd /在您的脚本中)并 fork 以便父进程退出,从而关闭原始描述符。
  • 也许跑umask 0。您可能不想依赖守护进程调用者的 umask。

For an example of a script that takes all of these aspects into account, see Mike S' answer.

有关考虑所有这些方面的脚本示例,请参阅Mike S 的回答

回答by Mike S

Some of the top-upvoted answers here are missing some important parts of what makes a daemon a daemon, as opposed to just a background process, or a background process detached from a shell.

这里的一些最高投票答案缺少使守护进程成为守护进程的一些重要部分,而不仅仅是后台进程或与 shell 分离的后台进程。

This http://www.faqs.org/faqs/unix-faq/programmer/faq/describes what is necessary to be a daemon. And this Run bash script as daemonimplements the setsid, though it misses the chdir to root.

这个http://www.faqs.org/faqs/unix-faq/programmer/faq/描述了成为守护进程的必要条件。这个Run bash script as daemon实现了 setid,尽管它错过了 root 的 chdir。

The original poster's question was actually more specific than "How do I create a daemon process using bash?", but since the subject and answers discuss daemonizing shell scripts generally, I think it's important to point it out (for interlopers like me looking into the fine details of creating a daemon).

原始海报的问题实际上比“如何使用 bash 创建守护进程?”更具体,但由于主题和答案通常讨论守护进程的 shell 脚本,我认为指出这一点很重要(对于像我这样的闯入者来说创建守护进程的细节)。

Here's my rendition of a shell script that would behave according to the FAQ. Set DEBUG to trueto see pretty output (but it also exits immediately rather than looping endlessly):

这是我根据常见问题解答运行的 shell 脚本的再现。将 DEBUG 设置true为查看漂亮的输出(但它也会立即退出而不是无休止地循环):

#!/bin/bash
DEBUG=false

# This part is for fun, if you consider shell scripts fun- and I do.
trap process_USR1 SIGUSR1

process_USR1() {
    echo 'Got signal USR1'
    echo 'Did you notice that the signal was acted upon only after the sleep was done'
    echo 'in the while loop? Interesting, yes? Yes.'
    exit 0
}
# End of fun. Now on to the business end of things.

print_debug() {
    whatiam=""; tty=""
    [[ "$tty" != "not a tty" ]] && {
        echo "" >$tty
        echo "$whatiam, PID $$" >$tty
        ps -o pid,sess,pgid -p $$ >$tty
        tty >$tty
    }
}

me_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
me_FILE=$(basename 
<shell_prompt>$ bash blahd

*** PARENT, PID 5180
  PID  SESS  PGID
 5180  1708  5180
/dev/pts/6
PARENT OUT
<shell_prompt>$ 
*** CHILD, NEW SESSION, NEW PGID, PID 5188
  PID  SESS  PGID
 5188  5188  5188
not a tty
CHILD OUT

*** DAEMON, PID 5198
  PID  SESS  PGID
 5198  5188  5188
not a tty
NOT A REAL DAEMON. NOT RUNNING WHILE LOOP.
DAEMON OUT
) cd / #### CHILD HERE ---------------------------------------------------------------------> if [ "" = "child" ] ; then # 2. We are the child. We need to fork again. shift; tty=""; shift $DEBUG && print_debug "*** CHILD, NEW SESSION, NEW PGID" "$tty" umask 0 $me_DIR/$me_FILE XXrefork_daemonXX "$tty" "$@" </dev/null >/dev/null 2>/dev/null & $DEBUG && [[ "$tty" != "not a tty" ]] && echo "CHILD OUT" >$tty exit 0 fi ##### ENTRY POINT HERE --------------------------------------------------------------> if [ "" != "XXrefork_daemonXX" ] ; then # 1. This is where the original call starts. tty=$(tty) $DEBUG && print_debug "*** PARENT" "$tty" setsid $me_DIR/$me_FILE child "$tty" "$@" & $DEBUG && [[ "$tty" != "not a tty" ]] && echo "PARENT OUT" >$tty exit 0 fi ##### RUNS AFTER CHILD FORKS (actually, on Linux, clone()s. See strace --------------> # 3. We have been reforked. Go to work. exec >/tmp/outfile exec 2>/tmp/errfile exec 0</dev/null shift; tty=""; shift $DEBUG && print_debug "*** DAEMON" "$tty" # The real stuff goes here. To exit, see fun (above) $DEBUG && [[ "$tty" != "not a tty" ]] && echo NOT A REAL DAEMON. NOT RUNNING WHILE LOOP. >$tty $DEBUG || { while true; do echo "Change this loop, so this silly no-op goes away." >/dev/null echo "Do something useful with your life, young man." >/dev/null sleep 10 done } $DEBUG && [[ "$tty" != "not a tty" ]] && sleep 3 && echo "DAEMON OUT" >$tty exit # This may never run. Why is it here then? It's pretty. # Kind of like, "The End" at the end of a movie that you # already know is over. It's always nice.

Output looks like this when DEBUGis set to true. Notice how the session and process group ID (SESS, PGID) numbers change:

DEBUG设置为时输出如下所示true。注意会话和进程组 ID(SESS、PGID)编号是如何变化的:

# double background your script to have it detach from the tty
# cf. http://www.linux-mag.com/id/5981 
(./program.sh &) & 

回答by carlo

#!/bin/bash

# We will start the listener as Deamon process
#    
LISTENER_BIN=/tmp/deamon_test/listener
test -x $LISTENER_BIN || exit 5
PIDFILE=/tmp/deamon_test/listener.pid

case "" in
      start)
            echo -n "Starting Listener Deamon .... "
            startproc -f -p $PIDFILE $LISTENER_BIN
            echo "running"
            ;;
          *)
            echo "Usage: 
while true ; do sleep 600 ; echo "How are u ? " ; done
start" exit 1 ;; esac

回答by Artem

It really depends on what is the binary itself going to do.

这实际上取决于二进制文件本身要做什么。

For example I want to create some listener.

例如我想创建一些监听器。

The starting Daemon is simple task :

启动守护进程是一项简单的任务:

lis_deamon :

lis_deamon :

#!/bin/bash

# Starting listener on some port
# we will run it as deamon and we will send commands to it.
#
IP=$(hostname --ip-address)
PORT=1024
FILE=/tmp/backpipe
count=0
while [ -a $FILE ] ; do #If file exis I assume that it used by other program
  FILE=$FILE.$count
  count=$(($count + 1))
done

# Now we know that such file do not exist,
# U can write down in deamon it self the remove for those files
# or in different part of program

mknod $FILE p

while true ; do 
  netcat -l -s $IP -p $PORT < $FILE |/bin/bash > $FILE
done
rm $FILE

this is how we start the daemon (common way for all /etc/init.d/ staff)

这就是我们启动守护进程的方式(所有 /etc/init.d/ 员工的常用方式)

now as for the listener it self, It must be some kind of loop/alert or else that will trigger the script to do what u want. For example if u want your script to sleep 10 min and wake up and ask you how you are doing u will do this with the

现在对于听众本身来说,它必须是某种循环/警报,否则会触发脚本执行您想要的操作。例如,如果您希望您的脚本睡 10 分钟然后醒来并询问您的情况,您将使用

test_host#netcat 10.184.200.22 1024
uptime
 20:01pm  up 21 days  5:10,  44 users,  load average: 0.62, 0.61, 0.60
date
Tue Jan 28 20:02:00 IST 2014
 punt! (Cntrl+C)

Here is the simple listener that u can do that will listen for your commands from remote machine and execute them on local :

这是您可以执行的简单侦听器,它将侦听来自远程机器的命令并在本地执行它们:

listener :

听众:

echo "script.sh" | at now

So to start UP it : /tmp/deamon_test/listener start

所以要启动它:/tmp/deamon_test/listener start

and to send commands from shell (or wrap it to script) :

并从 shell 发送命令(或将其包装到脚本):

#!/usr/bin/env bash

export PID_FILE_PATH="/tmp/my-service.pid"
export LOG_FILE_PATH="/tmp/my-service.log"
export LOG_ERROR_FILE_PATH="/tmp/my-service.error.log"

. ./services.sh

run-script() {
  local action="" # Action

  while true; do
    echo "@@@ Running action '${action}'"
    echo foo
    echo bar >&2

    [ "$action" = "run" ] && return 0
    sleep 5
    [ "$action" = "debug" ] && exit 25
  done
}

before-start() {
  local action="" # Action

  echo "* Starting with $action"
}

after-finish() {
  local action="" # Action
  local serviceExitCode= # Service exit code

  echo "* Finish with $action. Exit code: $serviceExitCode"
}

action=""
serviceName="Example Service"

serviceMenu "$action" "$serviceName" run-script "$workDir" before-start after-finish

Hope this will help.

希望这会有所帮助。

回答by noName

If I had a script.shand i wanted to execute it from bash and leave it running even when I want to close my bash session then I would combine nohupand &at the end.

如果我有一个script.sh,我想从bash中执行它,离开它,即使在运行时,我想结束我的bash命令,然后我将结合nohup&结尾。

example: nohup ./script.sh < inputFile.txt > ./logFile 2>&1 &

例子: nohup ./script.sh < inputFile.txt > ./logFile 2>&1 &

inputFile.txtcan be any file. If your file has no input then we usually use /dev/null. So the command would be:

inputFile.txt可以是任何文件。如果您的文件没有输入,那么我们通常使用/dev/null. 所以命令是:

nohup ./script.sh < /dev/null > ./logFile 2>&1 &

nohup ./script.sh < /dev/null > ./logFile 2>&1 &

After that close your bash session,open another terminal and execute: ps -aux | egrep "script.sh"and you will see that your script is still running at the background. Of cource,if you want to stop it then execute the same command (ps) and kill -9 <PID-OF-YOUR-SCRIPT>

之后关闭您的 bash 会话,打开另一个终端并执行:ps -aux | egrep "script.sh"您将看到您的脚本仍在后台运行。当然,如果你想停止它然后执行相同的命令(ps)和kill -9 <PID-OF-YOUR-SCRIPT>

回答by timo

Have a look at the daemon tool from the libslack package:

查看 libslack 包中的守护进程工具:

http://ingvar.blog.linpro.no/2009/05/18/todays-sysadmin-tip-using-libslack-daemon-to-daemonize-a-script/

http://ingvar.blog.linpro.no/2009/05/18/todays-sysadmin-tip-using-libslack-daemon-to-daemonize-a-script/

On Mac OS X use a launchd script for shell daemon.

在 Mac OS X 上,对 shell 守护程序使用 launchd 脚本。

回答by Congbin Guo

$ ( cd /; umask 0; setsid your_script.sh </dev/null &>/dev/null & ) &

$ ( cd /; umask 0; setsid your_script.sh </dev/null &>/dev/null & ) &

回答by o?????

Like many answers this one is not a "real" daemonization but rather an alternative to nohupapproach.

像许多答案一样,这不是“真正的”守护进程,而是替代nohup方法。

$ ./example-service
# Actions: [start|stop|restart|status|run|debug|tail(-[log|error])]

$ ./example-service start
# Starting Example Service service...

$ ./example-service status
# Serive Example Service is runnig with PID 5599

$ ./example-service stop
# Stopping Example Service...

$ ./example-service status
# Service Example Service is not running

There are obviously differences from using nohup. For one there is no detaching from the parent in the first place. Also "script.sh" doesn't inherit parent's environment.

与使用nohup. 首先,没有与父母分离。“script.sh”也不继承父级的环境。

By no means this is a better alternative. It is simply a different (and somewhat lazy) way of launching processes in background.

这绝不是更好的选择。它只是在后台启动进程的一种不同(并且有点懒惰)的方式。

P.S. I personally upvoted carlo's answer as it seems to be the most elegant and works both from terminal and inside scripts

PS 我个人赞成 carlo 的答案,因为它似乎是最优雅的,并且可以在终端和内部脚本中使用

回答by Eduardo Cuomo

See Bash Service Managerproject: https://github.com/reduardo7/bash-service-manager

查看Bash 服务管理器项目:https: //github.com/redardo7/bash-service-manager

Implementation example

实现示例

##代码##

Usage example

使用示例

##代码##