ls -lah
func main() {
    cmd := exec.Command("ls", "-lah")
    if runtime.GOOS == "windows" {
        cmd = exec.Command("tasklist")
    }
    err := cmd.Run()
    if err != nil {
        log.Fatalf("cmd.Run() failed with %s\n", err)
    }
}
Linuxls -lahWindowstasklistshellshellstdoutstderr
func main() {
    cmd := exec.Command("ls", "-lah")
    if runtime.GOOS = "windows" {
        cmd := exec.Command("tasklist")
    }
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    err := cmd.Run()
    if err != nil {
        fmt.Printf("cmd.Run() failed with %s\n", err)
    }
}

// total 8.0k
// drwxr-xr-x.  6   root    root    120     Aug     20 14:38
// ...
cmd.Stdoutcmd.Stderrosos.Stdoutos.Stderros\file.goos\exec\exec.goCombinedOutputcmd
func (c *Cmd) CombinedOutput() ([]byte, error) {
    if c.Stdout != nil {
        return nil, errors.New("exec: Stdout already set")
    }
    if c.Stderr != nil {
        return nil, errors.New("exec: Stderr already set")
    }
    var b bytes.Buffer
    c.Stdout = &b
    c.Stderr = &b
    err := c.Run()
    return b.Bytes(), err
}
CombinedOutputcmd
func main() {
    out, err := exec.Command("ls", "-lah").CombinedOutput()
    if err != nil {
        fmt.Printf("CombinedOutput failed with %s\n", err)
    }
    fmt.Printf("combined out: %s\n", string(out))
}

// combined out: total 8.0k
// drwxr-xr-x.  6   root    root    120     Aug     20 14:38
// ...

上面所有的示例都是将命令执行的结果保存到一个变量中,如果想把标准输出和标准错误分开来显示,需要像下面分开绑定。

func main() {
    cmd := exec.Command("ls", "-lah")
    var stdout, stderr bytes.Buffer
    cmd.Stdout = &stdout
    cmd.Stderr = &stderr
    err := cmd.Run()
    if err != nil {
        fmt.Printf("cmd.Run failed with %s\n", err)
    }
    outStr, errStr := string(stdout.Bytes()), string(stderr.Bytes())
    fmt.Printf("out: %s\n err: %s\n", outStr, errStr)
}

// out: total 8.0k
// drwxr-xr-x.  6   root    root    120     Aug     20 14:38
// ...
// err:
Run
func (c *Cmd) Run() error {
    if err := c.Start(); err != nil {
        return err
    }
    return c.Wait()
}

上面的命令都是很快执行完,如果命令执行的时间较长,我们不仅需要最后的执行结果,还想看命令执行的过程,那该怎么办?

func main() {
    cmd := exec.Command("ls", "-lah")

    var stdout, stderr []byte
    var errStdout, errStderr error
    stdoutIn, _ := cmd.StdoutPipe()
    stderrIn, _ := cmd.StderrPipe()
    err := cmd.Start()
    if err != nil {
        log.Fatalf("cmd.Start failed with %s\n", err)
    }

    var wg sync.WaitGroup
    wg.Add(1)
    go func() {
        stdout, errStdout = copyAndCapture(os.Stdout, stdoutIn)
        wg.Done()
    }

    stderr, errStderr = copyAndCapture(os.Stderr, stderrIn)
    wg.Wait()

    if err = cmd.Wait(); err != nil {
        log.Fatalf("cmd.Wait failed with %s\n", err)
    }
    if errStdout != nil || errStderr != nil {
        log.Fatalf("failed to capture stdout or stderr\n")
    }
    outStr, errStr := string(stdout), string(stderr)
    log.Fatalf("out: %s\n err: %s\n", outStr, errStr)
}

func copyAndCapture(w io.Writer, r io.Reader) ([]byte, error) {
    var out []byte
    buf := make([]byte, 1024, 1024)
    for {
        n, err := r.Read(buf[:])
        if n > 0 {
            d := buf[:n]
            out = append(out, d...)
            _, err := w.Write(d)
            if err != nil {
                return out, err
            }
        }
        if err != nil {
            if err == io.EOF {
                err = nil
            }
            return out, err
        }
    }
}
StdoutPipeWaitWaitStdoutPipeRunRunWait
func (c *Cmd) StdoutPipe() (io.ReadCloser, error) {
    if c.Stdout != nil {
        return nil, errors.New("exec: Stdout already set")
    }
    if c.Process != nil {
        return nil, errors.New("exec: StdoutPipe after process started")
    }
    pr, pw, err := os.Pipe()
    if err != nil {
        return nil, err
    }
    c.Stdout = pw
    c.closeAfterStart = append(c.closeAfterStart, pw)
    c.closeAfterWait = append(c.closeAfterWait, pr)
    return pr, nil
}

type ReadCloser interface {
    Reader
    Closer
}

type Reader interface{
    Read(p []byte) (n int, err error)
}
// Reader接口包裹基本的Read方法,它会读取长度为len(p)的字节到p中,并返回读取的字节个数n,n的取值范围为0<=n<=len(p)。也就是当n等于0时,说明读取出错。err!=nil,出现这种情况有两种主要原因:一是文件已经读取完;另一个是读取出错。