问题描述
我正在尝试编写一个 Linux shell 脚本(最好是 bash),应该命名为 detach.sh
,以安全地从终端分离程序,使得:
I'm trying to write a Linux shell script (preferably bash),supposedly named detach.sh
, to safely detach programs from a terminal,such that:
调用:
./detach.sh prog [arg1 arg2 ...]
.
是 exec
-able,例如.通过在你的 shell 中运行它:
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).
不使用screen
、tmux
等.(与 4 的原因相同,而且不需要额外的保姆过程).
Does not use screen
, tmux
, etc.(same reason with 4, plus no need for an extra babysitting process).
使用(合理地)可移植的命令和程序,没有像 start-stop-daemon
这样的发行版特定的东西.
Uses (reasonably) portable commands and programs,and no things like start-stop-daemon
which is quite distro-specific.
我想了几种方法(shebang行#!/bin/bash
忽略为简洁起见):
I have thought of several ways (shebang lines #!/bin/bash
neglectedfor the sake of briefness):
nohup
:
nohup "$@" >& /dev/null &
否认
:
"$@" >& /dev/null &
disown
setsid
:
setsid "$@" >& /dev/null &
使用子shell:
Using a subshell:
("$@" >& /dev/null &)
nohup
/setsid
结合子shell:
nohup
/setsid
combined with subshell:
# Or alternatively:
# (nohup "$@" >& /dev/null &)
(setsid "$@" >& /dev/null &)
当使用gedit
作为测试程序时(替换"$@"
部分),上述所有方法都可以满足条件1,但条件 2 可以不满足.
When using gedit
as the test program (substituting the "$@"
part),condition 1 can be satisfied with all the above methods,but condition 2 can be satisfied with none.
但是,如果将任意程序(但不是内置的 shell)附加到脚本 5,所有条件似乎都得到满足(至少在 gedit
案例中对我而言).例如:
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 gedit
case).For example:
(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 phenomenonsand how to correctly implement the requirements?
在条件 2 中,我的意思是程序应该与终端分离,否则照常运行.例如,对于 gedit
情况,如果 gedit
在脚本进程结束后立即退出,则条件失败.
With condition 2, I mean the program should be detached from the terminal but runs as usual otherwise. For example, with the gedit
case, the condition fails if gedit
just exits immediately right after the process of the script has ended.
推荐答案
经过仔细调查,发现了这些以前未被注意到的事实:
Upon closer investigation, these previously unnoticed facts were revealed:
脚本 3 和 5(仅限
setsid
变体)都将如果将/bin/true
附加到脚本,则满足所有条件.
Both scripts 3 and 5 (the
setsid
variant only) willsatisfy all the conditions if a/bin/true
is appended to the script.
这些脚本,如在事实 1 中修改的那样,在以下情况下也能正常工作/bin/true
替换为 for i in {0..9999};做 :;完成
.
These scripts, as modified in fact 1, will work as well if/bin/true
is replaced with for i in {0..9999}; do :; done
.
因此我们可以得出结论:
Therefore we can conclude that:
(来自事实 1)
(From fact 1)
不需要多级分离(如脚本 5),关键是使用正确的实用程序(setsid
).
Multiple levels of detaching (as in script 5) is unnecessary,and the key is to use the right utility (setsid
).
(来自事实 2)
bash 退出前适当的延迟是脚本成功所必需的.(调用外部程序/bin/true
需要一些时间,就像纯 bash 时间消费者 for i in {0..9999};做 :;完成
.)
A suitable delay before bash exit is necessary for the success of the script.(Calling external program /bin/true
consumes some time,just like the pure-bash time consumer for i in {0..9999}; do :; done
.)
我没有看过源代码,但我想一个可能的解释bash 可能会在 setsid
完成配置执行之前退出程序运行的环境,如果没有应用适当的延迟.
I have not looked at the source code, but I guess a possible explanationis that bash may exit before setsid
finishes configuring the executionenvironment of the program to run, if an appropriate delay is not applied.
最后,最佳解决方案应该是
And finally, an optimal solution should be
#!/bin/bash
setsid "$@" >& /dev/null &
sleep 0.01
编辑 1:
已在此处解释了延迟的必要性.非常感谢@wilx!
The necessity of a delay has been explained here. Many thanks to @wilx!
编辑 2:
(感谢@MateiDavid)我们似乎忘记了重定向标准输入,更好的方法是:
(Thanks to @MateiDavid) we seem to have forgotten to redirect the standard input, and a better way would be:
#!/bin/bash
setsid "$@" >& /dev/null < /dev/null &
这篇关于编写 Linux shell 脚本以安全地从终端分离程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!