Terminating a Process Started with os/exec in Golang

后端 未结 3 1792
情歌与酒
情歌与酒 2020-12-07 10:24

Is there a way to terminate a process started with os.exec in Golang? For example (from http://golang.org/pkg/os/exec/#example_Cmd_Start),

cmd := exec.Comma         


        
相关标签:
3条回答
  • 2020-12-07 10:54

    Terminating a running exec.Process:

    // Start a process:
    cmd := exec.Command("sleep", "5")
    if err := cmd.Start(); err != nil {
        log.Fatal(err)
    }
    
    // Kill it:
    if err := cmd.Process.Kill(); err != nil {
        log.Fatal("failed to kill process: ", err)
    }
    

    Terminating a running exec.Process after a timeout:

    // Start a process:
    cmd := exec.Command("sleep", "5")
    if err := cmd.Start(); err != nil {
        log.Fatal(err)
    }
    
    // Wait for the process to finish or kill it after a timeout (whichever happens first):
    done := make(chan error, 1)
    go func() {
        done <- cmd.Wait()
    }()
    select {
    case <-time.After(3 * time.Second):
        if err := cmd.Process.Kill(); err != nil {
            log.Fatal("failed to kill process: ", err)
        }
        log.Println("process killed as timeout reached")
    case err := <-done:
        if err != nil {
            log.Fatalf("process finished with error = %v", err)
        }
        log.Print("process finished successfully")
    }
    

    Either the process ends and its error (if any) is received through done or 3 seconds have passed and the program is killed before it's finished.

    0 讨论(0)
  • 2020-12-07 10:56

    The other answers are right about calling Kill(), but the parts regarding killing the process after a timeout are little outdated now.

    This can be done now with the context package and exec.CommandContext (example adapted from the example in the docs):

    func main() {
        ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
        defer cancel()
    
        if err := exec.CommandContext(ctx, "sleep", "5").Run(); err != nil {
            // This will fail after 100 milliseconds. The 5 second sleep
            // will be interrupted.
        }
    }
    

    From the docs:

    The provided context is used to kill the process (by calling os.Process.Kill) if the context becomes done before the command completes on its own.

    After the Run() completes, you can inspect ctx.Err(). If the timeout was reached, the type of the error returned will be DeadLineExceeded. If it's nil, check the err returned by Run() to see if the command completed without errors.

    0 讨论(0)
  • 2020-12-07 11:08

    A simpler version without select and channels.

    func main() {
        cmd := exec.Command("cat", "/dev/urandom")
        cmd.Start()
        timer := time.AfterFunc(1*time.Second, func() {
            err := cmd.Process.Kill()
            if err != nil {
                panic(err) // panic as can't kill a process.
            }
        })
        err := cmd.Wait()
        timer.Stop()
    
        // read error from here, you will notice the kill from the 
        fmt.Println(err)
    }
    

    Well, after consulting some experienced go programmer, this is apparently not a GOly enough way to solve the problem. So please refer to the accepted answer.


    Here is an even shorter version, and very straight forward. BUT, possibly having tons of hanging goroutines if timeout is long.

    func main() {
        cmd := exec.Command("cat", "/dev/urandom")
        cmd.Start()
        go func(){
            time.Sleep(timeout)
            cmd.Process.Kill()
        }()
        return cmd.Wait()
    }
    
    0 讨论(0)
提交回复
热议问题