I'm trying to read stdout from a long-running (blocking / shell-like) command with the ultimate goal of creating a sidecar process that Go can interact with.
I have the following MCVE:
func main() {
var err error
// Build the long-running command
args := []string{"-i0", "-o0", "-e0", "/usr/local/bin/ipython"}
cmd := exec.Command("stdbuf", args...)
// Keep the stdin file descriptor open
// Spawned command should block waiting for input
_, err = cmd.StdinPipe()
if err != nil {
panic(err)
}
// Setup pipe of `Reader` type
var stdoutBuf []byte
stdout, err := cmd.StdoutPipe()
if err != nil {
panic(err)
}
// Start the command
if err = cmd.Start(); err != nil {
panic(err)
}
go func() {
for {
// Asynchronously continue reading from stdout
n, err := stdout.Read(stdoutBuf)
fmt.Println(n)
if err != nil {
panic(err)
}
fmt.Println(stdoutBuf)
time.Sleep(time.Millisecond * 2000)
}
}()
// Block forever
if err = cmd.Wait(); err != nil {
panic(err)
}
}
Semantically this approach seems like it would work. I would expect that stdout.Read
would return something (ipython
shell preamble), but n
is always 0.
- Why does
Reader.Read
in this example never read anything? - Somewhat related question: is there a way I can block on reading
StdoutPipe
if there is nothing to be read?
I don't think anything is wrong with the process itself (which would cause an invalid file descriptor read) because Start
doesn't panic
and it seem like cmd.Wait
blocks forever.