我正在使用包:os / exec http://golang.org/pkg/os/exec/来执行操作系统中的命令,但我似乎找不到获取退出代码的方法。我可以通过
读取输出即
package main
import(
"os/exec"
"bytes"
"fmt"
"log"
)
func main() {
cmd := exec.Command("somecommand", "parameter")
var out bytes.Buffer
cmd.Stdout = &out
if err := cmd.Run() ; err != nil {
//log.Fatal( cmd.ProcessState.Success() )
log.Fatal( err )
}
fmt.Printf("%q\n", out.String() )
}
答案 0 :(得分:72)
很容易确定退出代码是否为0或其他内容。在第一种情况下,cmd.Wait()
将返回nil(除非在设置管道时出现另一个错误)。
不幸的是,在错误情况下,没有独立于平台的方法来获取退出代码。这也是它不属于API的原因。以下代码片段适用于Linux,但我没有在其他平台上测试过:
package main
import "os/exec"
import "log"
import "syscall"
func main() {
cmd := exec.Command("git", "blub")
if err := cmd.Start(); err != nil {
log.Fatalf("cmd.Start: %v")
}
if err := cmd.Wait(); err != nil {
if exiterr, ok := err.(*exec.ExitError); ok {
// The program has exited with an exit code != 0
// This works on both Unix and Windows. Although package
// syscall is generally platform dependent, WaitStatus is
// defined for both Unix and Windows and in both cases has
// an ExitStatus() method with the same signature.
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
log.Printf("Exit Status: %d", status.ExitStatus())
}
} else {
log.Fatalf("cmd.Wait: %v", err)
}
}
}
答案 1 :(得分:17)
这是我基于@ tux21b的答案的增强版
utils/cmd.go
package utils
import (
"bytes"
"log"
"os/exec"
"syscall"
)
const defaultFailedCode = 1
func RunCommand(name string, args ...string) (stdout string, stderr string, exitCode int) {
log.Println("run command:", name, args)
var outbuf, errbuf bytes.Buffer
cmd := exec.Command(name, args...)
cmd.Stdout = &outbuf
cmd.Stderr = &errbuf
err := cmd.Run()
stdout = outbuf.String()
stderr = errbuf.String()
if err != nil {
// try to get the exit code
if exitError, ok := err.(*exec.ExitError); ok {
ws := exitError.Sys().(syscall.WaitStatus)
exitCode = ws.ExitStatus()
} else {
// This will happen (in OSX) if `name` is not available in $PATH,
// in this situation, exit code could not be get, and stderr will be
// empty string very likely, so we use the default fail code, and format err
// to string and set to stderr
log.Printf("Could not get exit code for failed program: %v, %v", name, args)
exitCode = defaultFailedCode
if stderr == "" {
stderr = err.Error()
}
}
} else {
// success, exitCode should be 0 if go is ok
ws := cmd.ProcessState.Sys().(syscall.WaitStatus)
exitCode = ws.ExitStatus()
}
log.Printf("command result, stdout: %v, stderr: %v, exitCode: %v", stdout, stderr, exitCode)
return
}
我已经在OSX上测试了它,如果它在其他平台上没有按预期工作,请告诉我,以便我们可以做得更好。
答案 2 :(得分:9)
从golang版本1.12开始,退出代码可以以本机方式以跨平台的方式使用。 See ExitError和ExitCode()。
ExitCode返回已退出进程的退出代码;如果该进程尚未退出或被信号终止,则返回-1。
if err := cmd.Run() ; err != nil {
if exitError, ok := err.(*exec.ExitError); ok {
return exitError.ExitCode()
}
}
答案 3 :(得分:4)
2019年9月,Go 1.13推出了errors.As,它支持错误“解包”-方便在嵌套调用链中查找精确错误。
因此,在运行外部命令时要提取并检查两个最常见的错误:
err := cmd.Run()
var (
ee *exec.ExitError
pe *os.PathError
)
if errors.As(err, &ee) {
log.Println("exit code error:", ee.ExitCode()) // ran, but non-zero exit code
} else if errors.As(err, &pe) {
log.Printf("os.PathError: %v", pe) // "no such file ...", "permission denied" etc.
} else if err != nil {
log.Printf("general error: %v", err) // something really bad happened!
} else {
log.Println("success!") // ran without error (exit code zero)
}