bash 关于编写 Linux shell 脚本以安全地从终端分离程序
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10247721/
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
On writing a Linux shell script to safely detach programs from a terminal
提问by Casper Ti. Vector
I'm trying to write a Linux shell script (preferably bash),
supposedly named detach.sh, to safely detach programs from a terminal,
such that:
我正在尝试编写一个名为 的 Linux shell 脚本(最好是 bash)detach.sh来安全地从终端分离程序,例如:
Invocation:
./detach.sh prog [arg1 arg2 ...].Is
exec-able, eg. by running this in your shell:exec ./detach.sh prog [arg1 arg2 ...]With proper quoting (mainly handling of arguments containing whitespaces).
Discards the outputs (since they are unneeded).
Does not use
screen,tmux, etc. (same reason with 4, plus no need for an extra babysitting process).Uses (reasonably) portable commands and programs, and no things like
start-stop-daemonwhich is quite distro-specific.
调用:
./detach.sh prog [arg1 arg2 ...]。是-
exec能够,例如。通过在你的 shell 中运行它:exec ./detach.sh prog [arg1 arg2 ...]正确引用(主要处理包含空格的参数)。
丢弃输出(因为它们不需要)。
不使用
screen,tmux等(与 4 的原因相同,而且不需要额外的保姆过程)。使用(合理地)可移植的命令和程序,而不是像
start-stop-daemon发行版特定的东西。
I have thought of several ways (shebang lines #!/bin/bashneglected
for the sake of briefness):
我想到了几种方法(为简洁#!/bin/bash起见忽略了shebang行):
nohup:nohup "$@" >& /dev/null &disown:"$@" >& /dev/null & disownsetsid:setsid "$@" >& /dev/null &Using a subshell:
("$@" >& /dev/null &)nohup/setsidcombined with subshell:# Or alternatively: # (nohup "$@" >& /dev/null &) (setsid "$@" >& /dev/null &)
nohup:nohup "$@" >& /dev/null &disown:"$@" >& /dev/null & disownsetsid:setsid "$@" >& /dev/null &使用子shell:
("$@" >& /dev/null &)nohup/setsid结合子外壳:# Or alternatively: # (nohup "$@" >& /dev/null &) (setsid "$@" >& /dev/null &)
When using geditas the test program (substituting the "$@"part),
condition 1 can be satisfied with all the above methods,
but condition 2 can be satisfied with none.
当使用gedit作为测试程序(代替"$@"一部分),条件1可以与所有上述方法来满足,但条件2可以与没有得到满足。
However, if an arbitrary program (but not a shell builtin) is appended to script 5,
all the conditions seem to be satisfied (at least for me in the geditcase).
For example:
但是,如果将任意程序(但不是 shell 内置程序)附加到脚本 5,则所有条件似乎都已满足(至少对我而言是gedit这样)。例如:
(setsid "$@" >& /dev/null &)
# Not just `true' because it is also a shell builtin.
/bin/true
Anyone with an idea about an explanation of the above phenomenons and how to correctly implement the requirements?
任何人都对上述现象的解释以及如何正确实现要求有任何想法?
EDIT:
编辑:
With condition 2, I mean the program should be detached from the terminal but runs as usual otherwise. For example, with the geditcase, the condition fails if geditjust exits immediately right after the process of the script has ended.
对于条件 2,我的意思是程序应该与终端分离,否则照常运行。例如,在这种gedit情况下,如果gedit在脚本进程结束后立即退出,则条件失败。
采纳答案by Casper Ti. Vector
Upon closer investigation, these previously unnoticed facts were revealed:
经过进一步调查,发现了这些以前未被注意到的事实:
Both scripts 3 and 5 (the
setsidvariant only) will satisfy all the conditions if a/bin/trueis appended to the script.These scripts, as modified in fact 1, will work as well if
/bin/trueis replaced withfor i in {0..9999}; do :; done.
setsid如果将 a/bin/true附加到脚本,则脚本 3 和 5(仅限变体)都将满足所有条件。这些脚本在事实上 1 中进行了修改,如果
/bin/true替换为for i in {0..9999}; do :; done.
Therefore we can conclude that:
因此我们可以得出结论:
(From fact 1)
Multiple levels of detaching (as in script 5) is unnecessary, and the key is to use the right utility (
setsid).(From fact 2)
A suitable delay before bash exit is necessary for the success of the script. (Calling external program
/bin/trueconsumes some time, just like the pure-bash time consumerfor i in {0..9999}; do :; done.)I have not looked at the source code, but I guess a possible explanation is that bash may exit before
setsidfinishes configuring the execution environment of the program to run, if an appropriate delay is not applied.
(从事实 1)
不需要多级分离(如脚本 5 中所示),关键是使用正确的实用程序 (
setsid)。(来自事实 2)
在 bash 退出之前适当的延迟是脚本成功所必需的。(调用外部程序
/bin/true会消耗一些时间,就像纯 bash 时间消费者一样for i in {0..9999}; do :; done。)我没有看过源代码,但我想一个可能的解释是
setsid,如果没有应用适当的延迟,bash 可能会在完成配置要运行的程序的执行环境之前退出。
And finally, an optimal solution should be
最后,一个最优解应该是
#!/bin/bash
setsid "$@" >& /dev/null &
sleep 0.01
EDIT 1:
编辑 1:
The necessity of a delay has been explained here. Many thanks to @wilx!
这里已经解释了延迟的必要性。非常感谢@wilx!
EDIT 2:
编辑 2:
(Thanks to @MateiDavid) we seem to have forgotten to redirect the standard input, and a better way would be:
(感谢@MateiDavid)我们似乎忘记了重定向标准输入,更好的方法是:
#!/bin/bash
setsid "$@" >& /dev/null < /dev/null &
回答by wilx
I think you need to do setsid "$@" >& /dev/null & waitso that the controlling terminal does not disappear before setsidmanages to fork the child.
我认为您需要这样做,setsid "$@" >& /dev/null & wait以便在setsid设法分叉孩子之前控制终端不会消失。
UPDATE
更新
It seems to me that this works both on command line and as argument of -c:
在我看来,这既适用于命令行,也适用于以下参数-c:
(setsid tail -F /var/log/messages >& /dev/null) & disown
回答by Francis Litterio
You are trying to create a UNIX daemon process (i.e., a process that has no controlling terminal and that is its own sessionleader). The setsidcommand should do this for you, but you are responsible for closing all file descriptors that are open on the terminal you are abandoning. This can be done by redirecting them to /dev/nullor using the shell's syntax for closing file descriptors (e.g., 2>&-and 0<&-in Bash).
您正在尝试创建一个 UNIX 守护进程(即,一个没有控制终端并且是它自己的会话领导者的进程)。该setsid命令应该为您执行此操作,但您有责任关闭在您放弃的终端上打开的所有文件描述符。这可以通过将其重定向到完成/dev/null或者使用shell的语法关闭文件描述符(例如,2>&-和0<&-在击)。

