Golang - 将exec输出复制到缓冲区

时间:2016-08-17 22:59:39

标签: go

我正在编写一个函数,它执行一个程序并返回stdout和stderr。它还可以选择将输出显示到控制台。我显然没有等待某些事情,好像我连续两次运行该功能,输出是不同的。这是一个示例程序,用带有大量文件的dir替换dir var来填充缓冲区:

func main() {
    dir := "SOMEDIRECTORYWITHALOTOFFILES"
    out, err := run("ls -l "+dir, true)
    if err != nil {
        log.Fatalf("run returned %s", err)
    }
    log.Printf("Out: %s", out)
    out2, err := run("ls -l "+dir, false)
    if err != nil {
        log.Fatalf("run returned %s", err)
    }
    log.Printf("Out2: %s", out2)
    if out != out2 {
        log.Fatalf("Out mismatch")
    }
}

func run(cmd string, displayOutput bool) (string, error) {
    var command *exec.Cmd
    command = exec.Command("/bin/sh", "-c", cmd)
    var output bytes.Buffer

    stdout, err := command.StdoutPipe()
    if err != nil {
        return "", fmt.Errorf("Unable to setup stdout for command: %v", err)
    }
    go func() {
        if displayOutput == true {
            w := io.MultiWriter(os.Stdout, &output)
            io.Copy(w, stdout)
        } else {
            output.ReadFrom(stdout)
        }
    }()

    stderr, err := command.StderrPipe()
    if err != nil {
        return "", fmt.Errorf("Unable to setup stderr for command: %v", err)
    }
    go func() {
        if displayOutput == true {
            w := io.MultiWriter(os.Stderr, &output)
            io.Copy(w, stderr)
        } else {
            output.ReadFrom(stderr)
        }
    }()
    err = command.Run()
    if err != nil {
        return "", err
    }
    return output.String(), nil
}

2 个答案:

答案 0 :(得分:1)

以下是您的示例的简化和有效修订。请注意,测试命令已被换出,以便我可以在Windows中进行测试,并且仅为了简洁而省略了错误检查。

关键的变化是run阻止func main() { dir := "c:\\windows\\system32" command1 := exec.Command("cmd", "/C", "dir", "/s", dir) command2 := exec.Command("cmd", "/C", "dir", "/s", dir) out1, _ := run(command1) out2, _ := run(command2) log.Printf("Length [%d] vs [%d]\n", len(out1), len(out2)) } func run(cmd *exec.Cmd) (string, error) { var output bytes.Buffer var waitGroup sync.WaitGroup stdout, _ := cmd.StdoutPipe() writer := io.MultiWriter(os.Stdout, &output) waitGroup.Add(1) go func() { defer waitGroup.Done() io.Copy(writer, stdout) }() cmd.Run() waitGroup.Wait() return output.String(), nil } 函数打印输出并返回,直到goroutine指示它已完成。

left join

答案 1 :(得分:0)

我看到了一些问题:

  • 您应该等待goroutines完成(例如,使用 sync.WaitGroup)。
  • 您同时访问output两个 goroutines,这是不安全的。

您可以在两个单独的缓冲区中收集stdoutstderr并单独返回,如果这对您要执行的操作有效。