2016-08-17 2 views
0

Я пишу функцию, которую выполняет программа exec, и возвращает stdout и stderr. Он также имеет возможность отображать вывод на консоль. Я явно ничего не жду от чего-то, как если бы я запускал функцию дважды подряд, выходы разные. Вот пример программа, замените реж вар с режем с большим количеством файлов для заполнения буфера:Golang - копировать вывод exec в буфер

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 
} 

ответ

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 
} 
0

Я вижу некоторые проблемы:

  • Вы должны быть ожидающими в goroutines, чтобы закончить (например, используя sync.WaitGroup).
  • Вы получаете доступ к output одновременно в двух goroutines, что небезопасно.

Вы можете собрать stdout и stderr в двух отдельных буферов и возвращать их по отдельности, если это работает для того, что вы пытаетесь сделать.

Смежные вопросы