编写Linux shell脚本以安全地从终端分离程序

时间:2012-04-20 13:57:17

标签: bash shell scripting

我试图编写Linux shell脚本(最好是bash), 据称名为detach.sh,以便从终端安全地分离程序, 这样:

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

  2. exec能否,例如。在shell中运行它:

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

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

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

  6. 使用(合理)便携式命令和程序, 而且start-stop-daemon之类的东西并不是特定于发行版的。

  7. 我想到了几种方式(shebang lines #!/bin/bash被忽略了 为了简洁起见):

    1. nohup

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

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

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

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

      # Or alternatively:
      # (nohup "$@" >& /dev/null &)
      (setsid "$@" >& /dev/null &)
      
    6. 使用gedit作为测试程序(替换"$@"部分)时, 条件1可满足上述所有方法, 但条件2可以不满足。

      但是,如果将任意程序(但不是shell内置)附加到脚本5, 所有条件似乎都得到满足(至少在gedit案例中对我来说)。 例如:

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

      任何想要解释上述现象的人 以及如何正确实施要求?

      修改

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

3 个答案:

答案 0 :(得分:3)

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

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

  2. 这些脚本,如事实1中修改的那样,如果可以的话 /bin/true替换为for i in {0..9999}; do :; done

  3. 因此我们可以得出结论:

    • (来自事实1)

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

    • (来自事实2)

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

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

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

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

    编辑1

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

    编辑2

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

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

答案 1 :(得分:1)

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

<强>更新

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

(setsid tail -F /var/log/messages >& /dev/null) & disown

答案 2 :(得分:0)

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