Hello Gopher,
So here is my general goal :
Under linux, I have two terminals opened, A and B. I want to feed arbitrary shell command to terminal B whenever a specific event happened in terminal A.
My typical use case would be to change current working directory of B to match CWD of A whenever A changed its location.
I first tried to do it in pure bash, but the result was not great.
So I now try to do it in Go. My attempt to do so was not successful either.
To simplify the problem, I focused only on the program that should be running on terminal B.
My requirements for it are :
- it should yield a fully interactive bash shell the user could use
- it could accept arbitrary command coming from another sources, a goroutine for example
Unfortunately, my attempts never satisfied both of the requirements.
To get a fully interactive shell, I coud do :
cmd := exec.Command("bash", "-i")
cmd.Stdin = os.Stdin
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
then run cmd.Run()
later
To write to stdin, in another goroutine I could do :
go func(){
time.Sleep(1 * time.Second)
io.WriteString(os.Stdin, "echo hello\n")
time.Sleep(5 * time.Second)
io.WriteString(os.Stdin, "echo hello again\n")
}()
cmd.Stdin being equal to os.Stdin, somehow, it should work, right (?)
It does not, I get the hellos on screen but this is not interpreted by the executed bash command
So, what about using stdin,err := cmd.StdinPipe()
instead of cmd.Stdin = os.Stdin
And then :
go func() {
defer stdin.Close()
time.Sleep(5 * time.Second)
io.WriteString(stdin, "echo hello\n")
}
Now I do not have interactive shell anymore, and it exits the shell after the echo command (that one work btw), probably due to an EOF being piped to the executed file, somehow.
I could avoid that EOF by not closing stdin, but then, I’m stuck with a non interactive shell expecting input from the pipe, which is not great.
I tried to feed os.Stdin back to the pipe with a scanner on os.Stdin. It did not work.
cmd.Stdin is an io.Reader, so why not create my custom io.Reader. I started simple :
type customReader struct{}
func (r customReader) Read(p []byte) (n int, err error) {
return os.Stdin.Read(p)
}
Unfortunately, this didn’t work either. Somehow, after the first command (that work great), an EOF is sent, Not sure why.
My final thought was to create a file that receives both os.Stdin and my custom command into it, and then assign cmd.Stdin = myCustomFile
But I’m not sure how to do this.
So, to my questions :
- Is feeding both stdin and some arbitrary input to a command actually possible ?
- Should I take another approach to solve my initial problem ?
For reference, here is my code : https://play.golang.org/p/8xpz5GzLvL
PS : sorry for this long text