在Go 1.7之前,根据超时结束外部流程需要一些工作。 随着上下文包移入标准库,诸如os / exec之类的标准包可以利用其提供的超时和取消功能。
这里先看在Go 1.7之前使用channel控制超时。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
package main
import (
"bytes"
"fmt"
"os/exec"
"time"
)
func main() {
// 试用ping来做测试,win平台去掉"-c 2", "-i 1"参数
cmd := exec.Command("ping", "-c 2", "-i 1", "8.8.8.8")
// 使用 bytes.Buffer 获取输出
var buf bytes.Buffer
cmd.Stdout = &buf
cmd.Start()
// 使用一个channel作为完成信号,这样可以使用select条件
done := make(chan error)
go func() { done <- cmd.Wait() }()
// 开始timer
timeout := time.After(2 * time.Second)
// select语句允许我们基于哪个channel执行
select {
case <-timeout:
// 超时先发生,杀掉进程兵器输出消息
cmd.Process.Kill()
fmt.Println("Command timed out")
case err := <-done:
// 命令在超时前执行完成,打印输出和错误(如果有错误)
fmt.Println("Output:", buf.String())
if err != nil {
fmt.Println("Non-zero exit code:", err)
}
}
}
|
Go 1.7及以后,使用CommandContext控制超时。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
package main
import (
"context"
"fmt"
"os/exec"
"time"
)
func main() {
// 创建一个上下文并且设置超时时间
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
// 使用我们的上下文创建命令
cmd := exec.CommandContext(ctx, "ping", "-c 4", "-i 1", "8.8.8.8")
// 这次我们可以简单的使用Output()获取输出结果
out, err := cmd.Output()
// 我们要通过检查上下文错误来确定超时是否执行
// cmd.Output()返回的错误将特定于操作系统,具体取决于进程被杀死时发生的情况。
if ctx.Err() == context.DeadlineExceeded {
fmt.Println("Command timed out")
return
}
// 如果没有上下文错误,我们知道命令已经执行完成(或者执行出错)
fmt.Println("Output:", string(out))
if err != nil {
fmt.Println("Non-zero exit code:", err)
}
}
|
翻译自:Go: Timeout Commands with os/exec CommandContext 在原文基础上做了些调整。