如何通过管道将io.Stdin和io.Stdout传递给exec.Command

I'm trying to read and write to an exec.Command, but I'm struggling with piping.

This first one is a example of the behaviour I want. The go application just proxies stdin, stdout and stderr to the command:

package main

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

func main() {
    cmd := exec.Command("bash", "-i")

    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr

    cmd.Run()
    fmt.Println("Done")
}

Running this is like:

pensarando@home: ~/go/src/test$ ./test
pensarando@home: ~/go/src/test$ echo "hello"
hello
pensarando@home: ~/go/src/test$ exit
Done
pensarando@home: ~/go/src/test$ 

But this one does not work at all:

package main

import (
    "fmt"
    "io"
    "os"
    "os/exec"
    "sync"
)

func main() {
    cmd := exec.Command("bash", "-i")

    in, _ := cmd.StdinPipe()
    out, _ := cmd.StdoutPipe()
    err, _ := cmd.StderrPipe()

    exit := make(chan struct{})

    done := func() {
        in.Close()
        out.Close()
        err.Close()
        cmd.Wait()
        close(exit)
    }

    var once sync.Once

    go func() {
        io.Copy(os.Stdout, out)
        fmt.Println("done stdout")
        once.Do(done)
    }()

    go func() {
        io.Copy(in, os.Stdin)
        fmt.Println("done stdin")
        once.Do(done)
    }()

    go func() {
        io.Copy(os.Stderr, err)
        fmt.Println("done stderr")
        once.Do(done)
    }()

    cmd.Start()
    <-exit
    fmt.Println("Done")
}

When I run this one from the terminal, the output is for example:

pensarando@home:~/go/src/test$ go build
pensarando@home:~/go/src/test$ ./test
pensarando@home:~/go/src/test$ echo "Hello"

[1]+  Stopped                 ./test
pensarando@home:~/go/src/test$ echo "Hello"
Hello
pensarando@home:~/go/src/test$ fg
./test
done stdin
done stdout
done stderr
Done
pensarando@home:~/go/src/test$

What exactly happens here?