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

Bash: How to get a script to rerun itself as a background task?

我正在编写一个 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 "" 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 &

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

谢谢。

& 被应用到 exec 命令本身,因此 exec foo & 派生出一个新的异步子 shell(或与其等效,见下文)。 subshell 立即用 foo 替换自己。如果您希望父级(即您的脚本)也终止,则需要使用 exit 命令明确地执行此操作。

exec 可能不会在这里给你买任何东西。 Bash 足够聪明,不会为简单的后台命令实际启动 subshell。所以这样做就足够了:

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

我不知道没有子shell 的方法。但是当你编写 shell 脚本时,你会得到 subshells。就个人而言,我只是使用一个简单的变量并用 if [[ $async ]]; 之类的东西对其进行测试,而不是执行 truefalse,但由于它们也是 bash 内置函数,所以它非常好相等的。在其他 shell 中,他们可能 运行 在 subshell 中。

现在我想起来了,既然你正在重新处理异步执行中的所有选项,你最好从 case 语句中分叉并退出,所以你不需要第二次检查:

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

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

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

接着是传递 --sync 时您的代码应该执行的操作。