Bash:如何让脚本重新运行作为后台任务?

时间:2016-02-22 02:13:43

标签: linux bash shell unix sh

我正在编写一个旨在用作守护程序的Bash脚本。如果我的脚本的用户没有将--sync选项传递给脚本,我希望脚本使用该选项将其自身重新运行为后台任务。这是我的代码(最后一部分是从this SO post偷来的):

#!/usr/bin/env bash

args=("$@") # capture them here so we can use them if --sync's not passed
async=true

while [ $# -gt 0 ]
do
    case "$1" in
        --sync)
            async=false
            ;;
        # other options
    esac
    shift
done

# if --sync isn't passed, rerun the script as a background task
$async && exec nohup "${BASH_SOURCE[0]}" --sync "${args[@]}" 0<&- &> /dev/null &

出于某种原因,它似乎不起作用。当我执行bash -x myscript(这有助于调试脚本)时,即使$async为真,它似乎仍在继续,我认为这不会发生,因为exec通常会停止执行。

同样,如果我从终端运行此命令:

exec nohup true 0<&- &> /dev/null &

尽管使用exec,它也无法退出shell。为什么会这样,我该怎么做才能解决这个问题? (奖励积分:有没有办法做到这一点没有创建子shell?)

感谢。

2 个答案:

答案 0 :(得分:4)

&正在应用于exec命令本身,因此exec foo &会分叉一个新的异步子shell(或等效子shell,见下文)。子shell立即替换为foo。如果您希望父(也就是您的脚本)也终止,您需要使用exit命令明确地执行此操作。

exec可能不会在这里给你买任何东西。 Bash非常聪明,实际上并没有为简单的后台命令启动子shell。所以它应该足够了:

if $async; then
  nohup "${BASH_SOURCE[0]}" --sync "${args[@]}" 0<&- &> /dev/null &
  exit 0
fi

如果没有子shell,我不知道如何做到这一点。但是当你编写shell脚本时,你会得到子shell。就个人而言,我只是使用一个简单的变量并使用类似if [[ $async ]];的内容进行测试,而不是执行truefalse,但由于这些也是bash内置函数,因此它非常相似。在其他炮弹中,它们可能会在子炮弹中运行。

现在我想到了,因为你无论如何都在重新处理异步执行中的所有选项,你也可以在case语句中分叉并退出,这样你就不需要第二次检查了在所有:

case "$1" in
    --sync)
        nohup "${BASH_SOURCE[0]}" --sync "${args[@]}" 0<&- &> /dev/null &
        exit 0
        ;;

答案 1 :(得分:0)

我不同意rici的回答,因为问题清楚地说明只有当--sync没有传递到脚本时才需要后台。显示的内容似乎是一个无限循环,并没有检查所有传递的参数。我相信原始代码很好,除了最后的“async&amp;&amp; exec ......”。该行的以下替换应该有效:

if [ "$async" = true ]; then
   nohup "${BASH_SOURCE[0]}" --sync "${args[@]}" 0<&- &> /dev/null &
   exit 0
fi

后面是代码在传递--sync时应该做的事情。