我有一个运行超时命令的函数。它看起来像这样:
func run_command(cmdName string, cmdArgs []string, timeout int) (int, string) {
// the command we're going to run
cmd := exec.Command(cmdName, cmdArgs...)
// assign vars for output and stderr
var output bytes.Buffer
var stderr bytes.Buffer
// get the stdout and stderr and assign to pointers
cmd.Stderr = &stderr
cmd.Stdout = &output
// Start the command
if err := cmd.Start(); err != nil {
log.Fatalf("Command not found: %s", cmdName)
}
timer := time.AfterFunc(time.Second*time.Duration(timeout), func() {
err := cmd.Process.Kill()
if err != nil {
panic(err)
}
})
// Here's the good stuff
if err := cmd.Wait(); err != nil {
if exiterr, ok := err.(*exec.ExitError); ok {
// Command ! exit 0, capture it
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
// Check it's nagios compliant
if status.ExitStatus() == 1 || status.ExitStatus() == 2 || status.ExitStatus() == 3 {
return status.ExitStatus(), stderr.String()
} else {
// If not, force an exit code 2
return 2, stderr.String()
}
}
} else {
log.Fatalf("cmd.Wait: %v", err)
}
timer.Stop()
}
// We didn't get captured, continue!
return 0, output.String()
}
Run Code Online (Sandbox Code Playgroud)
现在我希望能够使超时成为可选的。为了稍微弥补这一点,我尝试简单地允许将超时设置为0,然后在计时器周围添加一个 if 语句。它最终看起来像这样。
if timeout > 0 {
timer := time.AfterFunc(time.Second*time.Duration(timeout), func() {
err := cmd.Process.Kill()
if err != nil {
panic(err)
}
})
}
Run Code Online (Sandbox Code Playgroud)
当然,这失败了,因为计时器不再定义timer.Stop()了。
所以我timer.Stop()也用 if 语句包装了它。
if timeout > 0 {
timer.Stop()
}
Run Code Online (Sandbox Code Playgroud)
这也行不通。
做这样的事情的正确方法是什么?Golang 严格类型对我来说是新的,所以我很难理解它
使用该context包可以轻松处理超时。
golang.org/x/net/context自 Go 1.7 起已成为标准库。下面是一个例子:
package main
import (
"context"
"os"
"os/exec"
"strconv"
"time"
)
func main() {
timeout, err := strconv.Atoi(os.Args[1])
if err != nil {
panic(err)
}
ctx := context.Background()
if timeout > 0 {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
defer cancel()
}
cmd := exec.CommandContext(ctx, "sleep", "5")
if err := cmd.Run(); err != nil {
panic(err)
}
}
Run Code Online (Sandbox Code Playgroud)
当超时设置为 3 秒时,运行sleep 5:
$ go run main.go 3
panic: signal: killed
goroutine 1 [running]:
panic(0xc7040, 0xc42008c020)
/usr/local/Cellar/go/1.7.4_1/libexec/src/runtime/panic.go:500 +0x1a1
main.main()
/Users/m-morita/work/tmp/20170106/main.go:27 +0x11c
exit status 2
Run Code Online (Sandbox Code Playgroud)
当设置为10秒或0(=永不超时)时,正常结束:
$ go run main.go 10
$ go run main.go 0
Run Code Online (Sandbox Code Playgroud)