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
Best way to make a shell script daemon?
提问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 SIGHUP
does 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("/")
(orcd /
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 true
to 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 DEBUG
is 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.sh
and i wanted to execute it from bash and leave it running even when I want to close my bash session then I would combine nohup
and &
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.txt
can 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 包中的守护进程工具:
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 nohup
approach.
像许多答案一样,这不是“真正的”守护进程,而是替代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