标准库中的Package exec是一种跨平台的方式来启动流程,捕获其输出以及更多。
基本命令执行
最简单的用法是:使用exec.Command(exe字符串,args ... string)创建exec.Cmd结构调用cmd.CombinedOutput()来执行cmd并获取stdout和stderr的组合*仅获取stdout并调用cmd .Output()
cmd := exec.Command("go", "version")
out, err := cmd.CombinedOutput()
if err != nil {
log.Fatalf("cmd.CombinedOutput() failed with '%s'\n", err)
}
fmt.Printf("Output:\n%s\n", string(out))
go version go1.13.1 linux/amd64
高级的命令执行:
var stdout, stderr bytes.Buffer
cmd := exec.Command("go", "version")
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err := cmd.Start()
if err != nil {
log.Fatalf("cmd.Start() failed with '%s'\n", err)
}
err = cmd.Wait()
if err != nil {
log.Fatalf("cmd.Wait() failed with '%s'\n", err)
}
out := append(stdout.Bytes(), stderr.Bytes()...)
fmt.Printf("Output:\n%s\n", string(out))
go version go1.13.4 linux/amd64
这在功能上与上面的示例相同,但是我们使用了更细粒度的控件。
我们通过将cmd.Stdout和cmd.Stderr设置为内存支持的io.Writer来捕获进程的stdout和stderr。
cmd.Start()将命令作为新的OS进程启动。 就像OS进程一样,它与我们的代码同时执行。
我们需要调用cmd.Wait()来等待该过程完成。 为避免无限等待,可能需要添加超时。
超时
如果无法保证进程能完成运行,则要添加超时限制。
cmd := exec.Command("go", "version")
err := cmd.Start()
if err != nil {
log.Fatalf("cmd.Start() failed with '%s'\n", err)
}
var timedOut int32
timeout := 1 * time.Millisecond
stopTimer := time.AfterFunc(timeout, func() {
cmd.Process.Kill()
atomic.StoreInt32(&timedOut, 1)
})
err = cmd.Wait()
stopTimer.Stop()
didTimeout := atomic.LoadInt32(&timedOut) != 0
fmt.Printf("didTimeout: %v, err: %v\n", didTimeout, err)
didTimeout: true, err: signal: killed
在调用cmd.Wait()之前,我们使用time.AfterFunc()在超时到期后终止进程。
cmd.Process.Kill()使cmd.Wait()退出并出现错误(信号:在Unix上已终止)。
如果cmd.Wait()在超时之前完成,则取消计时器。
变量timedOut是int32而不是bool。
我们使用atomic.StoreInt32和atomic.LoadInt32,因为存在细微的计时问题。
如果进程照常完成,则在cmd.Wait()退出之后且在我们调用stopTimer.Stop()之前,计时器可能会到期并仍然执行该函数。
这是极不可能的,但要记住一点。
os.Exec pitfalls
exec.Cmd无法重用
调用exec.Cmd的Run,Output或CombinedOutput方法后,将无法重用。
cmd := exec.Command("go", "version")
_, err := cmd.CombinedOutput()
if err != nil {
log.Fatalf("first cmd.CombintedOutput() failed with '%s'\n", err)
}
_, err = cmd.CombinedOutput()
if err != nil {
log.Fatalf("second cmd.CombintedOutput() failed with '%s'\n", err)
}
2019/11/06 04:50:26 second cmd.CombintedOutput() failed with 'exec: Stdout already set'
exit status 1