制作shell脚本守护程序的最佳方法是什么?
我想知道是否有一个更好的方法来创build一个守护进程,等待只使用sh的守护进程:
#! /bin/sh trap processUserSig SIGUSR1 processUserSig() { echo "doing stuff" } while true; do sleep 1000 done
特别是,我想知道是否有办法摆脱循环,仍然有东西在听信号。
使用你的系统的守护进程,例如start-stop-daemon 。
否则,是的,必须有一个循环的地方。
只是背景你的脚本( ./myscript &
)不会守护它。 请参阅http://www.faqs.org/faqs/unix-faq/programmer/faq/ ,第1.7节,其中描述了成为守护进程的必要条件。 您必须从terminal上断开连接,使SIGHUP
不会SIGHUP
它。 你可以采取一个快捷方式来使脚本看起来像一个守护进程,
nohup ./myscript 0<&- &>/dev/null &
会做这项工作。 或者,将stderr和stdout捕获到一个文件中:
nohup ./myscript 0<&- &> my.admin.log.file &
但是,您可能还需要考虑更重要的方面。 例如:
- 您将仍然有一个文件描述符打开到脚本,这意味着它所挂载的目录将是不可挂载的。 要成为一个真正的守护进程,你应该
chdir("/")
(或cd /
在你的脚本中)和fork,以便父进程退出,从而closures原始描述符。 - 也许运行
umask 0
。 您可能不想依赖守护进程的调用者的umask。
有关将所有这些方面都考虑在内的脚本示例,请参阅Mike S的答案 。
# double background your script to have it detach from the tty # cf. http://www.linux-mag.com/id/5981 (./program.sh &) &
这里的一些top-upvoted答案丢失了什么使守护进程成为守护进程的一些重要部分,而不仅仅是后台进程或从shell中分离的后台进程。
这个http://www.faqs.org/faqs/unix-faq/programmer/faq/描述了一个守护进程的必要条件。; 而这个运行bash脚本作为守护进程实现setsid,虽然它错过了chdir的根。
原始海报的问题实际上比“如何使用bash创build守护进程”更具体,但是由于主题和答案一般讨论守护shell脚本,所以我认为指出它是很重要的(对于像我这样的闯入者来说,创build守护进程的细节)。
这是我的翻译一个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="$1"; tty="$2" [[ "$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 $0) cd / #### CHILD HERE ---------------------------------------------------------------------> if [ "$1" = "child" ] ; then # 2. We are the child. We need to fork again. shift; tty="$1"; 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 [ "$1" != "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="$1"; 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.
当DEBUG
设置为true
时,输出看起来像这样。 注意会话和进程组ID(SESS,PGID)号码是如何改变的:
<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
这实际上取决于二进制文件本身要做什么。
比如我想创build一些监听器。
启动守护进程是一项简单的任务:
lis_deamon:
#!/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 "$1" in start) echo -n "Starting Listener Deamon .... " startproc -f -p $PIDFILE $LISTENER_BIN echo "running" ;; *) echo "Usage: $0 start" exit 1 ;; esac
这是我们如何启动守护进程(所有/etc/init.d/ staff的通用方式)
现在对于听众来说它是自己的,它必须是某种循环/提醒,否则会触发脚本去做你想做的事情。 例如,如果你想让你的脚本睡10分钟,醒来问你如何做你会做到这一点
while true ; do sleep 600 ; echo "How are u ? " ; done
这是你可以做的简单的监听程序,它将监听来自远程机器的命令并在本地执行它们:
听众:
#!/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
所以要启动它:/ tmp / deamon_test / listener start
并从shell发送命令(或将其包装到脚本):
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)
希望这会有所帮助。
看一下libslack包中的守护进程工具:
在Mac OS X上,使用shell守护程序的launchd脚本。
如果我有一个script.sh
,我想从bash执行它,即使当我想closures我的bash会话时,它仍然运行,那么我将结合nohup
和&
。
例如: nohup ./script.sh < inputFile.txt > ./logFile 2>&1 &
inputFile.txt
可以是任何文件。 如果你的文件没有input,那么我们通常使用/dev/null
。 所以命令是:
nohup ./script.sh < /dev/null > ./logFile 2>&1 &
之后closures你的bash会话,打开另一个terminal并执行: ps -aux | egrep "script.sh"
ps -aux | egrep "script.sh"
,你会看到你的脚本仍然在后台运行。 如果你想停止它,那么执行相同的命令(ps)并kill -9 <PID-OF-YOUR-SCRIPT>
请参阅Bash Service Manager项目: https : //github.com/reduardo7/bash-service-manager
实现的例子
#!/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="$1" # 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="$1" # Action echo "* Starting with $action" } after-finish() { local action="$1" # Action local serviceExitCode=$2 # Service exit code echo "* Finish with $action. Exit code: $serviceExitCode" } action="$1" serviceName="Example Service" serviceMenu "$action" "$serviceName" run-script "$workDir" before-start after-finish
用法示例
$ ./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
尝试使用&执行,如果你将这个文件保存为program.sh
您可以使用
$. program.sh &