如何在golang程序退出后保持子进程运行?

时间:2017-02-26 16:50:30

标签: go subprocess

我注意到使用Start()创建的子进程将在程序退出后终止,例如:

package main

import "os/exec"

func main() {
    cmd := exec.Command("sh", "test.sh")
    cmd.Start()
}

main()退出时,test.sh将停止运行

3 个答案:

答案 0 :(得分:2)

子流程应该在流程结束后继续运行,只要它干净地结束,如果你点击^C就不会发生。 您可以做的是拦截发送到您的流程的信号,以便您可以干净利落地结束。

sigchan := make(chan os.Signal, 1)
signal.Notify(sigchan,
    syscall.SIGINT,
    syscall.SIGKILL,
    syscall.SIGTERM,
    syscall.SIGQUIT)
go func() {
    s := <-sigchan
    // do anything you need to end program cleanly
}()

答案 1 :(得分:1)

尝试修改程序a以使用Run而不是start。通过这种方式,Go程序将在退出之前等待sh脚本完成。

package main

import (
    "log"
    "os/exec"
)

func main() {
    cmd := exec.Command("sh", "test.sh")
    err := cmd.Run()
    if err != nil {
        log.Fatalln(err)
    }
}

同样地,你总是可以使用一个等待小组,但我认为这样做太过分了。

你也可以只做一个有或没有等待组的例程。取决于你是否想要等待程序sh程序完成

package main

import (
    "os/exec"
)

func runOffMainProgram() {
    cmd := exec.Command("sh", "test.sh")
    cmd.Start()
}

func main() {
    // This will start a go routine, but without a waitgroup this program will exit as soon as it runs
    // regardless the sh program will be running in the background. Until the sh program completes
    go runOffMainProgram()
}

答案 2 :(得分:0)

一旦 go 程序完成,子进程(如果没有在 go 程序中等待)将继续运行(除非子进程自然在父 go 程序之前完成)。

原始发布者可能遇到的问题是他们可能提前终止了他们的 go 程序(例如使用 <Ctrl-c>),并且因为 go 程序没有干净地退出,它产生的子进程也被终止了。

>

下面是一个简化的测试用例,有助于验证这种行为......

首先,我创建了一个我想运行的 bash shell 脚本(例如 test.sh,不要忘记 chmod +x ./test.sh 所以脚本被认为是“可执行的”)。脚本非常简单。它休眠 10 秒,然后创建一个名为 testfile 的新文件(如果它不存在),或者如果该文件已经存在,它将更新“上次修改”时间戳。这很重要,因为这是我在 go 程序完成后确认 bash 脚本仍在运行的方式(由于 10 秒的睡眠,我希望在 bash 脚本完成之前很久)。

#!/usr/local/bin/bash

sleep 10
touch testfile

接下来,我有一个简单的 go 程序,它生成一个运行上面的 bash 脚本的子进程,但重要的是不等待它完成。你会看到我还在我的 go 程序中添加了 2 秒的睡眠,这让我有时间按下 <Ctrl-c>。现在,即使我有 2 秒的睡眠时间,这个程序(如果在没有我按下 <Ctrl-c> 的情况下运行)将在子进程 bash 脚本执行之前完成(睡眠 10 秒):

package main

import (
    "fmt"
    "log"
    "os/exec"
    "time"
)

func main() {
    cmd := exec.Command("./test.sh")
    err := cmd.Start()
    if err != nil {
        log.Fatal(err)
    }
    time.Sleep(2 * time.Second)
    fmt.Println("program finished, but what about the subprocess?")
}

如果我运行 go 程序并让它自然完成,我可以ls -l testfile 并检查它的时间戳。然后我将等待 10 秒并再次运行 ls -l testfile,我将看到时间戳更新(显示子进程成功完成)。

现在如果我重新运行 go 程序,这次在程序结束前按下 <Ctrl-c>(这就是我添加 2 秒睡眠的原因),那么不仅 go 程序会提前退出,而且子进程也将终止。所以我可以等待 10 秒或 10 小时或更长时间,没关系。 testfile 上的时间戳不会更新,证明子进程已终止。