10

我正在尝试编写一个名为 的 Linux shell 脚本(最好是 bash),detach.sh以安全地从终端分离程序,例如:

  1. 调用:./detach.sh prog [arg1 arg2 ...]

  2. 能够,exec例如。通过在你的 shell 中运行它:

    exec ./detach.sh prog [arg1 arg2 ...]
    
  3. 正确引用(主要是处理包含空格的参数)。

  4. 丢弃输出(因为它们是不需要的)。

  5. 不使用screen,tmux等(与 4 相同的原因,加上不需要额外的保姆过程)。

  6. 使用(合理地)可移植的命令和程序,而不是像start-stop-daemon发行版那样的东西。

我想到了几种方法(#!/bin/bash为了简洁起见,忽略了shebang线):

  1. nohup

    nohup "$@" >& /dev/null &
    
  2. disown

    "$@" >& /dev/null &
    disown
    
  3. setsid

    setsid "$@" >& /dev/null &
    
  4. 使用子外壳:

    ("$@" >& /dev/null &)
    
  5. nohup/setsid结合子shell:

    # Or alternatively:
    # (nohup "$@" >& /dev/null &)
    (setsid "$@" >& /dev/null &)
    

gedit作为测试程序使用时(代入"$@"部分),条件1可以满足上述所有方法,但条件2可以不满足。

但是,如果将任意程序(但不是内置的 shell)附加到脚本 5,则似乎满足所有条件(至少对我gedit而言)。例如:

(setsid "$@" >& /dev/null &)
# Not just `true' because it is also a shell builtin.
/bin/true

有人对上述现象的解释以及如何正确实施要求有所了解吗?

编辑:

对于条件 2,我的意思是程序应该与终端分离,否则照常运行。例如,对于这种情况,如果在脚本处理结束后立即退出gedit,则条件失败。gedit

4

3 回答 3

4

经过仔细调查,这些以前未被注意到的事实被揭露:

  1. 如果将 a附加到脚本,则脚本 3 和 5(setsid仅限变体)都将满足所有条件。/bin/true

  2. 这些脚本,如实际上 1 所修改,如果 /bin/true替换为for i in {0..9999}; do :; done.

因此我们可以得出结论:

  • (根据事实 1)

    不需要多级分离(如脚本 5 中所示),关键是使用正确的实用程序 ( setsid)。

  • (根据事实 2)

    bash 退出之前的适当延迟对于脚本的成功是必要的。(调用外部程序/bin/true会消耗一些时间,就像纯 bash 时间消费者一样for i in {0..9999}; do :; done。)

    我没有查看源代码,但我想一个可能的解释是setsid,如果没有应用适当的延迟,bash 可能会在完成配置要运行的程序的执行环境之前退出。

最后,最佳解决方案应该是

#!/bin/bash
setsid "$@" >& /dev/null &
sleep 0.01

编辑 1

这里已经解释了延迟的必要性。非常感谢@wilx!

编辑 2

(感谢@MateiDavid)我们似乎忘记了重定向标准输入,更好的方法是:

#!/bin/bash
setsid "$@" >& /dev/null < /dev/null &
于 2014-03-30T08:08:49.047 回答
1

我认为您需要这样做,以使控制终端在设法分叉孩子setsid "$@" >& /dev/null & wait之前不会消失。setsid

更新

在我看来,这既适用于命令行,也适用于以下参数-c

(setsid tail -F /var/log/messages >& /dev/null) & disown
于 2016-05-10T09:46:33.693 回答
0

您正在尝试创建一个 UNIX 守护进程(即,一个没有控制终端并且是它自己的会话领导者的进程)。该setsid命令应该为您执行此操作,但您有责任关闭您要放弃的终端上打开的所有文件描述符。这可以通过将它们重定向到/dev/null或使用 shell 的关闭文件描述符的语法来完成(例如,2>&-0<&-Bash 中)。

于 2012-05-01T19:15:20.370 回答