4

I want to use GO to run an asynchronous command on windows 10. The command I am running is blocking, and if run directly from the terminal it provides a constant steam of status messages until ctrl-c

I want to run that command from GO via exec and catch the output from the exec command to the terminal in real time, i.e. not only when the GO application terminates.

I have tried numerous examples but with not success, I just get a blank terminal and even after exiting the GO application, I don't see the output from the command I executed.

2 Answers 2

7

You can use cmd.StdoutPipe to do that:

cmd := exec.Command(cmdName, cmdArgs...)
cmdReader, _ := cmd.StdoutPipe()
scanner := bufio.NewScanner(cmdReader)
done := make(chan bool)
go func() {
    for scanner.Scan() {
        fmt.Printf(scanner.Text())
    }
    done <- true
}()
cmd.Start()
<- done
err = cmd.Wait()
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks, I had tried this already with no luck, then after a bit more digging, I found the the application I am running is outputting to Stderr, so a slight change for my circumstances and this did work.
I think this method is not very reliable, at least not when I am running the code using it inside unit tests on Travis CI. The reason for this is that the execution of the command could, in principle, complete before the goroutine had arrived at the point to read the output, inside scanner.Scan(). When this happens, the output pipe will be closed before the scanner was able to register the output and the result will be lost. One possible workaround is to wait some time before cmd.Start(), but it slows everything down - I do not know any robust way to fix that :(
@Bronek See edit, you can just use a channel to block until you've finished outputting everything before continuing
@dave - thank you, that's robust solution. It takes advantage of the fact that scanner.Scan() will keep the loop running until the sub-process has exited. Only when that happens we will proceed to cmd.Wait() which closes the output pipes. In case if we wanted to process both Stdout and Stderr, we would use two separate go-routines, two scanners and sync.WaitGroup() with a count of 2, instead of a channel.
Does not work here , the output of external exe is not displayed realtime, but after it quitting. Why it is so hard with golang? With Python, just subprocess.call(yourCmd) does the trick well.
1

You can use io.MultiWriter to capture output and forward it to stdout and stderr.

var stdoutBuf, stderrBuf bytes.Buffer

cmd := exec.Command("/some-command")
cmd.Stdout = io.MultiWriter(os.Stdout, &stdoutBuf)
cmd.Stderr = io.MultiWriter(os.Stderr, &stderrBuf)
err := cmd.Start()  // Starts command asynchronously

if err != nil {
    fmt.Printf(err.Error())
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.