读者从未读过“ StdoutPipe”

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.

  1. Why does Reader.Read in this example never read anything?
  2. 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.

Why does Reader.Read in this example never read anything?

Because you are reading into a buffer of size 0, which is what var stdoutBuf []byte creates. Instead, use something like:

stdoutBuf := make([]byte, 4096)

You may also want to use bufio.

Somewhat related question: is there a way I can block on reading StdoutPipe if there is nothing to be read?

stdout.Read already blocks, as is typical (though not required) for an io.Reader:

If some data is available but not len(p) bytes, Read conventionally returns what is available instead of waiting for more. [...] Implementations of Read are discouraged from returning a zero byte count with a nil error, except when len(p) == 0.