检测过程不会完成是一个难题。事实上,它是计算机科学中经典的“无法解决”问题之一:停机问题。
通常,当您调用 exec.Command 并且不会向其传递任何输入时,它会导致程序从您的操作系统的空设备中读取(请参阅exec.Cmd字段中的文档)。在您的代码(以及下面的代码)中,您显式创建了一个管道(尽管您应该检查错误返回StdinPipe
以防它未正确创建),因此您应该随后调用in.Close()
. 在任何一种情况下,子进程都会得到一个 EOF,并且应该在自己之后清理并退出。
为了帮助处理无法正确处理输入或以其他方式陷入困境的进程,一般的解决方案是使用超时。在 Go 中,您可以为此使用 goroutine:
// Set your timeout
const CommandTimeout = 5 * time.Second
func main() {
cmd := exec.Command("login")
// Set up the input
in, err := cmd.StdinPipe()
if err != nil {
log.Fatalf("failed to create pipe for STDIN: %s", err)
}
// Write the input and close
go func() {
defer in.Close()
fmt.Fprintln(in, "user")
}()
// Capture the output
var b bytes.Buffer
cmd.Stdout, cmd.Stderr = &b, &b
// Start the process
if err := cmd.Start(); err != nil {
log.Fatalf("failed to start command: %s", err)
}
// Kill the process if it doesn't exit in time
defer time.AfterFunc(CommandTimeout, func() {
log.Printf("command timed out")
cmd.Process.Kill()
}).Stop()
// Wait for the process to finish
if err := cmd.Wait(); err != nil {
log.Fatalf("command failed: %s", err)
}
// Print out the output
fmt.Printf("Output:\n%s", b.String())
}
在上面的代码中,实际上有三个感兴趣的主 goroutine:主 goroutine 生成子进程并等待它退出;如果进程没有及时停止,则会在后台发送一个计时器 goroutine 以终止该进程;和一个 goroutine,当它准备好读取它时将输出写入程序。