制作 shell 脚本守护进程的最佳方法?
我想知道是否有更好的方法来创建一个只使用 sh 等待某些东西的守护进程:
#! /bin/sh
trap processUserSig SIGUSR1
processUserSig() {
echo "doing stuff"
}
while true; do
sleep 1000
done
特别是,我想知道是否有任何方法可以摆脱循环并仍然让该东西监听信号。
I'm wondering if there is a better way to make a daemon that waits for something using only sh than:
#! /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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(13)
仅将脚本置于后台 (
./myscript &
) 不会对其进行守护进程。请参阅http://www.faqs.org/faqs/unix-faq/programmer /faq/,第 1.7 节,描述了成为守护程序所需的条件。您必须将其与终端断开连接,以便SIGHUP
不会终止它。您可以采取捷径使脚本看起来像守护进程;会做这项工作。或者,要将 stderr 和 stdout 捕获到文件中:
重定向解释(请参阅 bash 重定向)
0<&-
关闭标准输入&> file
将 stdout 和 stderr 发送到文件但是,您可能还需要考虑其他重要方面。例如:
chdir("/")
(或脚本中的cd /
),并 fork 以便父进程退出,因此原始描述符是关闭。umask 0
。您可能不想依赖守护程序调用者的 umask。有关考虑所有这些方面的脚本示例,请参阅 Mike S 的回答。
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 thatSIGHUP
does not kill it. You can take a shortcut to make a script appear to act like a daemon;will do the job. Or, to capture both stderr and stdout to a file:
Redirection explained (see bash redirection)
0<&-
closes stdin&> file
sends stdout and stderr to a fileHowever, there may be further important aspects that you need to consider. For example:
chdir("/")
(orcd /
inside your script), and fork so that the parent exits, and thus the original descriptor is closed.umask 0
. You may not want to depend on the umask of the caller of the daemon.For an example of a script that takes all of these aspects into account, see Mike S' answer.
这里一些最受好评的答案缺少使守护进程成为守护进程的一些重要部分,而不仅仅是后台进程或与 shell 分离的后台进程。
这个http://www.faqs.org/faqs/unix-faq/programmer /faq/ 描述了成为守护进程所需的条件。这个将bash脚本作为守护进程运行实现了setsid,尽管它错过了root的chdir 。
原始发帖者的问题实际上比“如何使用 bash 创建守护进程?”更具体,但由于主题和答案通常讨论守护进程 shell 脚本,我认为指出这一点很重要(对于像我这样研究的闯入者)创建守护进程的详细信息)。
这是我根据常见问题解答执行的 shell 脚本的再现。将 DEBUG 设置为
true
以查看漂亮的输出(但它也会立即退出,而不是无限循环):当
DEBUG
设置为true
时,输出如下所示>。请注意会话和进程组 ID(SESS、PGID)编号如何变化: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.
This http://www.faqs.org/faqs/unix-faq/programmer/faq/ describes what is necessary to be a daemon. And this Run bash script as daemon implements the setsid, though it misses the chdir to root.
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).
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):Output looks like this when
DEBUG
is set totrue
. Notice how the session and process group ID (SESS, PGID) numbers change:使用系统的守护进程工具,例如 start-stop-daemon 。
否则,是的,某个地方一定有一个循环。
Use your system's daemon facility, such as start-stop-daemon.
Otherwise, yes, there has to be a loop somewhere.
$ ( cd /; umask 0;setsid your_script.sh /dev/null & ) &
$ ( cd /; umask 0; setsid your_script.sh </dev/null &>/dev/null & ) &
这实际上取决于二进制文件本身要做什么。
例如我想创建一些监听器。
启动守护进程是简单的任务:
lis_deamon :
这就是我们现在启动守护进程的方式(所有 /etc/init.d/ 人员的通用方式),
现在对于监听器本身而言,
它必须是某种循环/警报,否则将触发脚本
做你想做的事。例如,如果您希望脚本休眠 10 分钟
醒来后问你过得怎么样,你将使用以下方法来做到这一点:
这是你可以做的简单监听器,它会监听你的声音
来自远程计算机的命令并在本地执行它们:
listener:
因此要启动它:/tmp/deamon_test/listener start
并从 shell 发送命令(或将其包装到脚本):
希望这会有所帮助。
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 :
this is how we start the daemon (common way for all /etc/init.d/ staff)
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
Here is the simple listener that u can do that will listen for your
commands from remote machine and execute them on local :
listener :
So to start UP it : /tmp/deamon_test/listener start
and to send commands from shell (or wrap it to script) :
Hope this will help.
以下是对在 Bourne shell(或 Bash)中创建有效守护进程的原始提案的最小更改:
说明:
我想没有比这更简单的了。
Here is the minimal change to the original proposal to create a valid daemon in Bourne shell (or Bash):
Explanation:
Guess it does not get any simpler than that.
查看 libslack 包中的守护程序工具:
http://ingvar.blog.linpro.no/2009/05/18/todays-sysadmin-tip-using-libslack-daemon-to-daemonize-a-script /
在 Mac OS X 上,使用 launchd 脚本作为 shell 守护程序。
Have a look at the daemon tool from the libslack package:
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.
如果我有一个
script.sh
并且我想从 bash 执行它并让它运行,即使我想关闭我的 bash 会话,那么我会结合nohup
和&
在最后。示例:nohup ./script.sh <输入文件.txt > ./logFile 2>&1 &
inputFile.txt
可以是任何文件。如果您的文件没有输入,那么我们通常使用/dev/null
。所以命令是:nohup ./script.sh
nohup ./script.sh
nohup ./script.sh
/dev/null > ./logFile 2>&1 &
关闭 bash 会话后,打开另一个终端并执行:
ps -aux | egrep "script.sh"
你会看到你的脚本仍在后台运行。当然,如果您想停止它,请执行相同的命令 (ps) 和kill -9
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 combinenohup
and&
at the end.example:
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: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) andkill -9 <PID-OF-YOUR-SCRIPT>
请参阅 Bash 服务管理器 项目:https://github.com/reduardo7 /bash-service-manager
实现示例
使用示例
See Bash Service Manager project: https://github.com/reduardo7/bash-service-manager
Implementation example
Usage example
Congbin Duo 的答案主要获取正确的元素来正确地守护进程,包括双分叉、分离 stdin/stdout/stderr、重置cwd 和umask,以及运行setsid。然而,它有两个问题:
它有一个竞争条件,如果终端在他的一句后快速退出,则 to-daemonize 进程将永远不会真正运行。例如,以下命令将可靠地不执行
sleep
命令(仅供参考,在 Fedora Workstation 38 v1.6 LiveCD 上测试):据我所知,问题是,如果终端在进程完全守护进程之前退出(即
setsid
已执行该命令),这将破坏守护进程并该命令永远不会真正运行。双叉和
setsid
序列未按正确的顺序完成。它应该像fork; 一样完成。集西德; fork;
而不是fork;叉; setid;
。例如,请参阅“UNIX 守护进程和双分叉”更详细的解释。据我所知,以下一行代码解决了这些问题:
解释:
[...] & wait $!
是第一个fork()
。我们等待它以避免过早退出并破坏守护进程。setsid [...]
是对setsid()
的调用。sh -c '[...] &' -- your-script.sh arg1 arg2
启动一个 shell 来执行其中的第二个fork()
操作。your-script.sh arg1 arg2
是守护进程的脚本及其参数(不需要转义它们),并将存储在$@
变量中.<代码>cd /;掩码0; "$@" /dev/null 2>&1 执行其余的守护进程(重置 cwd 和 umask、分离 stdin/stdout/stderr),然后调用守护进程。
Congbin Duo's answer mostly gets the right elements to properly daemonize a process, including double forking, detaching stdin/stdout/stderr, resetting the cwd and umask, and running setsid. However, it has two issues:
It has a race condition where if the terminal quickly exits after his one-liner, the to-daemonize process will never actually run. For example, the following command will reliably not execute the
sleep
command (for reference, tested on a Fedora Workstation 38 v1.6 LiveCD):As far as I can tell, the problem is that if the terminal exits before the process has been fully daemonized (i.e.
setsid
has exec'd the command), this will break the daemonized process and the command will never actually run.The double fork and
setsid
sequence is not done in the right order. It should be done likefork; setsid; fork;
instead offork; fork; setsid;
. See for example "UNIX daemonization and the double fork" for a more detailed explanation.The following one-liner, to my best knowledge, fixes those issues:
Explanation:
[...] & wait $!
is the firstfork()
. We wait for it to avoid exiting prematurely and breaking the daemonization process.setsid [...]
is the call tosetsid()
.sh -c '[...] &' -- your-script.sh arg1 arg2
starts a shell to do the secondfork()
inside it.your-script.sh arg1 arg2
is the script to daemonize and its arguments (you do not need to escape them) and will be stored in the$@
variable.cd /; umask 0; "$@" </dev/null >/dev/null 2>&1
does the rest of the daemonization (reset cwd and umask, detach stdin/stdout/stderr) and then invokes the daemon.与许多答案一样,这不是“真正的”守护进程,而是
nohup
方法的替代方法。与使用
nohup
有明显的区别。对于一个人来说,一开始就没有脱离父母。此外,“script.sh”不会继承父级的环境。这绝不是一个更好的选择。它只是一种在后台启动进程的不同(并且有些懒惰)的方式。
PS 我个人赞成卡洛的答案,因为它似乎是最优雅的,并且可以在终端和内部脚本中使用
Like many answers this one is not a "real" daemonization but rather an alternative to
nohup
approach.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.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
尝试使用 & 执行
如果您将此文件另存为program.sh,
您可以使用
try executing using &
if you save this file as program.sh
you can use