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 ]];
之类的东西对其进行测试,而不是执行 true
或 false
,但由于它们也是 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 时您的代码应该执行的操作。
我正在编写一个 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 ]];
之类的东西对其进行测试,而不是执行 true
或 false
,但由于它们也是 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 时您的代码应该执行的操作。