7

GO: Is there some way to communicate with a subprocess (shell script / python script), which is waiting for input on stdin?

e.g. python script (subprocess)

import sys
while True:
    sys.stdout.write('%s\n'%eval(sys.stdin.readline()))

In the go program, I want to create a subprocess of this python script and provide it input on its stdin, whenever necessary and repeatedly, and take its output. Writing on stdout of Go program or reading/writing from a file will also do.

This is roughly what I am trying, but nothing happens -

c := exec.Command("python", "-u add.py")
si,_ := c.StdinPipe()
so,_ := c.StdoutPipe()    
c.Start()
si.Write([]byte("2+2\n")
Palash Kumar
  • 429
  • 6
  • 18
  • unrelated: you could replace `eval(sys.stdin.readline())` with `input()` on Python 2. Or replace the whole loop with `import code; shell = code.InteractiveConsole(); shell.interact()` Or just start `python -i` process. – jfs Mar 27 '14 at 07:02
  • That is why, you shouldn't ignore errors, try `exec.Command("python", "-u", "add.py")` or `exec.Command("python", "-u", "-i")` or `exec.Command("python", "-i")` (if it doesn't make the output fully buffered) – jfs Mar 27 '14 at 07:15
  • no error. Still doesn't work – Palash Kumar Mar 27 '14 at 09:04

1 Answers1

5

Here is a working version of your go code (python code is unchanged).

Note: checking of all errors, fixed -u flag, use of bufio to read a line, and Wait to wait for end of process.

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

func main() {
    c := exec.Command("python", "-u", "add.py")
    si, err := c.StdinPipe()
    if err != nil {
        log.Fatal(err)
    }

    so, err := c.StdoutPipe()
    if err != nil {
        log.Fatal(err)
    }
    reader := bufio.NewReader(so)

    err = c.Start()
    if err != nil {
        log.Fatal(err)
    }

    // Now do some maths
    for i := 0; i < 10; i++ {
        sum := fmt.Sprintf("2+%d\n", i)
        _, err = si.Write([]byte(sum))
        if err != nil {
            log.Fatal(err)
        }
        answer, err := reader.ReadString('\n')
        if err != nil {
            log.Fatal(err)
        }
        fmt.Printf("Answer to %q is %q\n", sum, answer)
    }

    // Close the input and wait for exit
    si.Close()
    so.Close()
    c.Wait()
}

Which produces

Answer to "2+0\n" is "2\n"
Answer to "2+1\n" is "3\n"
Answer to "2+2\n" is "4\n"
Answer to "2+3\n" is "5\n"
Answer to "2+4\n" is "6\n"
Answer to "2+5\n" is "7\n"
Answer to "2+6\n" is "8\n"
Answer to "2+7\n" is "9\n"
Answer to "2+8\n" is "10\n"
Answer to "2+9\n" is "11\n"
Nick Craig-Wood
  • 52,955
  • 12
  • 126
  • 132
  • 1
    What happens if you add a delay to the loop? Does it print the results one by one or all at once at the end? Do you need `si.Flush()` or `si` is unbuffered? Do you need `\r\n` on Windows? – jfs Mar 27 '14 at 17:07
  • 1
    @J.F.Sebastian it works fine if you add a delay - the results appear slowly one by one. Everything is unbuffered in go (except `bufio` above) so no flushes needed. The `-u` takes care of the python end. Not sure whether `sys.stdin.readline()` will read `\n` or `\r\n` on windows but the go prog will just send and expect `\n`. – Nick Craig-Wood Mar 27 '14 at 18:01