Go中的持久性程序

I'm trying to learn Go, and I thought I'd start with a project I've wanted to do for some time (but which seemed too 'fiddly' to bother with). The essential idea is that I have a program which interacts with the user on stdin/stdout and I'd like to write a new program which interacts with the program in the same way (as if it were a person running the program).

Now the program here is simple, in that it's synchronous: you enter a command, get some output, and then it sits there waiting for the next batch of input. That didn't seem so hard, but I'm having trouble getting this I/O skeleton working.

package main

import (
    "os/exec"
    "time"
    "bufio"
    "math/rand"
    "fmt"
    "strings"
)

func main() {
    cmd := exec.Command("e")    // A simple program that echos input until it becomes "exit"

    progin, err := cmd.StdoutPipe()
    if err != nil {
        fmt.Println("Trouble with e's stdout")
        panic(err)
    }

    err = cmd.Start()
    if err != nil {
        fmt.Println("Trouble starting e")
        panic(err)
    }

    r := rand.New(rand.NewSource(99))

    buf := bufio.NewReader(progin)
    for {
        // Write stuff
        var toProg string
        if (r.Float64() < .1) {
            toProg = "exit"
        } else {
            toProg = fmt.Sprintf("%d", r.Int)
        }
        fmt.Println("Printing: ", toProg)
        cmd.Stdin = strings.NewReader(toProg + "
")

        // Read stuff
        time.Sleep(500 * time.Millisecond) // give the program time to generate output

        input, err := buf.ReadString('
')
        if err != nil {
            fmt.Println("I did *not* like that: ", input)
            panic(err)
        }
        fmt.Println("Received: ", input)

    }
}

Any takers?

Your biggest issue is you're reassigning the command's Stdin, instead of piping to it.

Here's a working version:

package main

import (
    "bufio"
    "fmt"
    "math/rand"
    "os/exec"
    "time"
)

func main() {
    cmd := exec.Command("./e") // A simple program that echos input until it becomes "exit"

    progin, err := cmd.StdoutPipe()
    if err != nil {
        fmt.Println("Trouble with e's stdout")
        panic(err)
    }

    progout, err := cmd.StdinPipe()
    if err != nil {
        fmt.Println("Trouble with e's stdin")
        panic(err)
    }

    err = cmd.Start()
    if err != nil {
        fmt.Println("Trouble starting e")
        panic(err)
    }

    r := rand.New(rand.NewSource(99))

    buf := bufio.NewReader(progin)
    for {
        // Write stuff
        var toProg string
        if r.Float64() < .1 {
            toProg = "exit"
        } else {
            toProg = fmt.Sprintf("%d", r.Int())
        }
        fmt.Println("Printing: ", toProg)
        progout.Write([]byte(toProg + "
"))

        // Read stuff
        time.Sleep(500 * time.Millisecond) // give the program time to generate output

        input, err := buf.ReadString('
')
        if err != nil {
            fmt.Println("I did *not* like that: ", input)
            panic(err)
        }
        fmt.Println("Received: ", input)

    }
}

And e.go with a few fixups:

package main

import (
    "bufio"
    "fmt"
    "os"
    "strings"
)

func main() {
    for {
        buf := bufio.NewReader(os.Stdin)
        input, err := buf.ReadString('
')

        if err != nil {
            fmt.Println("Echo failed: ", input)
            panic(err)
        }

        if strings.HasPrefix(input, "exit") {
            fmt.Println("Bye!")
            return
        }

        fmt.Print(input)
    }
}

You're kind of reinventing the wheel, everything you want to do can be handled by the fmt package, so just use that and add in the extra stuff like the random number checker where it is needed.

Here is an example of how to achieve this easily: http://play.golang.org/p/b9GNovSRFj

And for a quick look, the main functionality of this strategy:

var s string
_, err := fmt.Scanf("%s", &s)
if err != nil {
    log.Fatal(err)
}
fmt.Println(s)

Note that it will not run in the playground due to permission issues, but if you run it locally it should work fine.