【问题标题】:Golang - Copy exec output to a bufferGolang - 将 exec 输出复制到缓冲区
【发布时间】:2016-08-18 09:45:12
【问题描述】:

我正在编写一个执行程序并返回标准输出和标准错误的函数。它还可以选择将输出显示到控制台。我显然不是在等待什么,好像我连续两次运行该函数,输出是不同的。这是一个示例程序,将 dir var 替换为包含大量文件的 dir 以填满缓冲区:

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
}

【问题讨论】:

    标签: go


    【解决方案1】:

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

    关键的变化是 sync.WaitGroup 阻止了 run 函数打印输出并返回,直到 goroutine 指示它已完成。

    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
    }
    

    【讨论】:

      【解决方案2】:

      我看到了一些问题:

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

      您可以在两个单独的缓冲区中收集 stdoutstderr 并分别返回它们,如果这适用于您正在尝试做的事情。

      【讨论】:

        猜你喜欢
        • 2011-02-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-12-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多