我正在编写一个旨在用作守护程序的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?)
感谢。
答案 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 ]];
的内容进行测试,而不是执行true
或false
,但由于这些也是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时应该做的事情。