6

I have a small Go tool which basically allows the user to define an command that than will be run using os/exec.

My problem is that I want to show the user the output (stdout/stderr) of the command.

An example could look like this: The user defines a command that in the end is sh test.sh. Content of test.sh:

echo "Start"
sleep 7s
echo "Done"

With my current implementation the user can only see the output once the complete command finished. In the example above the user wouldn't see the output Start until the sleep command and the second echo finish.

I currently retrieve the output of the command like this:

cmd := exec.Command(command, args...)
cmd.Dir = dir
// Attach to the standard out to read what the command might print
stdout, err := cmd.StdoutPipe()
if err != nil {
    log.Panic(err)
}
// Execute the command
if err := cmd.Start(); err != nil {
    log.Panic(err)
}

buf := new(bytes.Buffer)
buf.ReadFrom(stdout)
log.Print(buf.String())

Is it somehow possible to read the stdout/stderr in real-time. Meaning that as soon as the user defined command creates and output it is printed?

Christian
  • 365
  • 1
  • 4
  • 12
  • 1
    set the stdout property of the cmd instance to whatever sink you need. os.Stdout is the most obvious one. `cmd.Stdout = os.Stdout`. Do the same for stderr. –  Jan 14 '18 at 19:22
  • Is it possible to improve that? Our answer works but it has two behaviors that make it less attractive to me. For one I need to examine what has been printed out. The second one is more severe. The go program now exits before the command has finished. (I want to execute the command, show the result to the user. After the command has been executed I want to examine the output to decide what to do next.) – Christian Jan 14 '18 at 19:26
  • https://golang.org/pkg/io/#MultiWriter then ? –  Jan 14 '18 at 19:31
  • to wait for command completion use Run not Start. If you need to use Start, call Wait to block until command completion. –  Jan 14 '18 at 19:39

1 Answers1

11

Thank you mh-cbon. That pushed me in the right direction.

The code now looks like this and does exactly what I want it to do. I also found that when I use Run() instead of Start() the execution of the program only continues after the command has finished.

cmd := exec.Command(command, args...)
cmd.Dir = dir

var stdBuffer bytes.Buffer
mw := io.MultiWriter(os.Stdout, &stdBuffer)

cmd.Stdout = mw
cmd.Stderr = mw

// Execute the command
if err := cmd.Run(); err != nil {
    log.Panic(err)
}

log.Println(stdBuffer.String())
Christian
  • 365
  • 1
  • 4
  • 12
  • If dound a duplicate of my question (https://stackoverflow.com/questions/30993717/how-can-i-redirect-in-go-the-stdout-and-stderr-of-a-command-to-both-the-consol?rq=1). Because I don't have the necessary reputation I can't mark it as duplicate. – Christian Jan 14 '18 at 19:44