有没有办法终止在Golang中使用os.exec启动的进程?例如(来自http://golang.org/pkg/os/exec/#example_Cmd_Start),
cmd := exec.Command("sleep", "5")
err := cmd.Start()
if err != nil {
log.Fatal(err)
}
log.Printf("Waiting for command to finish...")
err = cmd.Wait()
log.Printf("Command finished with error: %v", err)
Run Code Online (Sandbox Code Playgroud)
有没有办法提前终止该过程,也许是在3秒后?
提前致谢
Mos*_*vah 116
终止正在运行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)
}
Run Code Online (Sandbox Code Playgroud)
exec.Process超时后终止运行:
// 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")
}
Run Code Online (Sandbox Code Playgroud)
过程结束并且错误(如果有的话)通过done或3秒钟已经过去并且程序在完成之前被终止.
Dav*_*llo 21
其他关于调用的答案是正确的Kill(),但是关于在超时后终止进程的部分现在已经有点过时了。
现在可以使用context包和exec.CommandContext完成此操作(示例改编自文档中的示例):
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.
}
}
Run Code Online (Sandbox Code Playgroud)
从文档:
如果上下文在命令自行完成之前完成,则提供的上下文用于终止进程(通过调用 os.Process.Kill)。
在之后Run()完成,你可以检查ctx.Err()。如果达到超时,返回的错误类型将为DeadLineExceeded。如果是nil,请检查err返回的 byRun()以查看命令是否完成且没有错误。
没有选择和频道的简单版本.
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)
}
Run Code Online (Sandbox Code Playgroud)
好吧,在咨询了一些经验丰富的程序员之后,这显然不是一个足以解决问题的方法.所以请参考接受的答案.
这是一个更短的版本,非常直接.但是,如果超时很长,可能会有大量悬挂的goroutine.
func main() {
cmd := exec.Command("cat", "/dev/urandom")
cmd.Start()
go func(){
time.Sleep(timeout)
cmd.Process.Kill()
}()
return cmd.Wait()
}
Run Code Online (Sandbox Code Playgroud)
虽然exec.CommandContext非常方便并且在大多数情况下工作正常,但我在该过程的子级存活方面遇到了一些问题 - 这导致了cmd.Wait()挂起。
如果有人遇到类似的情况,这就是我解决问题的方法。
Setpgid朴素的例子(为了可读性):
cmd := exec.Command("sleep", "5")
// Request the OS to assign process group to the new process, to which all its children will belong
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
go func() {
time.Sleep(time.Second)
// Send kill signal to the process group instead of single process (it gets the same value as the PID, only negative)
syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
}
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
log.Printf("Command finished successfully")
Run Code Online (Sandbox Code Playgroud)
一个更好的例子(对于新的地鼠来说可能不太直观):
// Create a context with timeout, which will close ctx.Done() channel upon timeout
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel() // Make sure the context is canceled, which will close ctx.Done() channel on function exit
cmd := exec.Command("sleep", "5")
// Request the OS to assign process group to the new process, to which all its children will belong
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
go func() {
// Wait until timeout or deferred cancellation
<- ctx.Done()
// Send kill signal to the process group instead of single process (it gets the same value as the PID, only negative)
_ = syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
}()
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
log.Printf("Command finished successfully")
Run Code Online (Sandbox Code Playgroud)
PS 为了简洁起见,我将cmd.Start+替换cmd.Wait为cmd.Run