简体   繁体   English

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

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

I'm writing a Bash script that is intended to be used as a daemon. 我正在编写一个旨在用作守护进程的Bash脚本。 If the user of my script does not pass a --sync option to the script, I want the script to rerun itself as a background task using that option. 如果我的脚本的用户没有向脚本传递--sync选项,我希望脚本使用该选项将其自身重新运行为后台任务。 Here is my code (the last part was stolen from this SO post ): 这是我的代码(最后一部分是从这篇SO帖子中偷来 ):

#!/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 &

For some reason, it doesn't seem to be working. 出于某种原因,它似乎没有起作用。 When I do bash -x myscript (which helps debug the script), it seems that it just keeps on going even if $async is true, which I didn't think would happen since exec normally stops execution. 当我执行bash -x myscript (这有助于调试脚本)时,即使$async为true,它似乎仍然继续运行,我认为这不会发生,因为exec通常会停止执行。

Likewise, if I run this command from my terminal: 同样,如果我从终端运行此命令:

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

it also fails to exit the shell, despite the use of exec . 尽管使用了exec ,它也无法退出shell。 Why is this, and what can I do to work around it? 为什么会这样,我该怎么做才能解决这个问题? (Bonus points: Is there any way to do this without creating a subshell?) (加分点:如果创建子shell,有没有办法做到这一点?)

Thanks. 谢谢。

The & is being applied to the exec command itself, so exec foo & forks a new asynchronous subshell (or equivalent thereto, see below). &正在应用于exec命令本身,因此exec foo & forks一个新的异步子shell(或其等价物,见下文)。 That subshell immediately replaces itself with foo . 子shell立即用foo替换自己。 If you want the parent (that is, your script) to terminate as well, you'll need to do so explicitly with an exit command. 如果您希望父(也就是您的脚本)也终止,您需要使用exit命令显式地执行此操作。

The exec is probably not buying you anything here. 这位exec可能不会在这里给你买任何东西。 Bash is clever enough to not actually start a subshell for a simple backgrounded command. Bash非常聪明,实际上并没有为简单的后台命令启动子shell。 So it should be sufficient to do: 所以它应该足够:

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

I don't know of a way to do this without a subshell. 我不知道没有子shell的方法。 But when you write shell scripts, you get subshells. 但是当你编写shell脚本时,你会得到子shell。 Personally I'd just use a simple variable and test it with something like if [[ $async ]]; 就个人而言,我只是使用一个简单的变量并使用if [[ $async ]];等测试它if [[ $async ]]; instead of executing true or false , but since those are also bash builtins, it's pretty well equivalent. 而不是执行truefalse ,但由于这些也是bash内置的,它非常相同。 In other shells they might run in subshells. 在其他炮弹中,它们可以在子炮弹中运行。

Now that I think of it, since you're reprocessing all the options in async execution anyway, you might as well just fork and exit from within the case statement, so you don't need the second check at all: 现在我想到了,因为你无论如何都在重新处理异步执行中的所有选项,你可能只是在case语句中fork和exit,所以你根本不需要第二次检查:

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

I disagree with rici's answer because the question clearly states background-ing is only wanted when --sync is NOT passed into the script. 我不同意rici的回答,因为问题清楚地说明只有在未将--sync传递给脚本时才需要背景。 What was shown appears to be an infinite loop, and isn't checking all the parameters passed. 显示的内容似乎是一个无限循环,并没有检查所有传递的参数。 I believe the original code was fine, except for the final "async && exec ...". 我相信原始代码很好,除了最后的“async && exec ...”。 The following replacement for that line should work: 该行的以下替换应该有效:

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

followed by what your code is supposed to do when --sync is passed. 接下来是--sync传递时你的代码应该做什么。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM