Sean Kelleher
Sean Kelleher

Reputation: 2092

Getting output from `exec.Cmd` in "real-time"

This question is similar to Golang - Copy Exec output to Log except it is concerned with the buffering of output from exec commands.

I have the following test program:

package main

import (
    "fmt"
    "log"
    "os/exec"
)

func main() {
    cmd := exec.Command("python", "inf_loop.py")
    var out outstream
    cmd.Stdout = out
    if err := cmd.Start(); err != nil {
        log.Fatal(err)
    }
    fmt.Println(cmd.Wait())
}

type outstream struct{}

func (out outstream) Write(p []byte) (int, error) {
    fmt.Println(string(p))
    return len(p), nil
}

inf_loop.py, which the above refers to, simply contains:

print "hello"
while True:
    pass

The go program hangs when I run it and doesn't output anything, but if I use os.Stdout instead of out then it outputs "hello" before it hangs. Why is there a discrepancy between the two io.Writers and how can it be fixed?

Some more diagnostic information:

Upvotes: 3

Views: 1351

Answers (1)

Thundercat
Thundercat

Reputation: 120941

Python buffers stdout by default. Try this program:

import sys
print "hello"
sys.stdout.flush()
while True:
    pass

or run Python with unbuffered stdout and stderr:

cmd := exec.Command("python", "-u", "foo.py")

Note the -u flag.

You see different results when using cmd.Stout = os.Stdout because Python uses line buffering when stdout is a terminal.

Upvotes: 4

Related Questions