Reading/writing from the child process
Every process, that is executed, has the standard output, input and error output. The Go standard library provides the way to read and write to these.
This recipe will walk through the approaches on how to read the output and write to the input of the child process.
Getting ready
Verify if the following commands work in the Terminal:
- Test if the
ls
(dir
for Windows) command exists in the Terminal. - You should be able to execute the
ls
(dir
in Windows) command in your Terminal.
How to do it…
- Open the console and create the folder
chapter01/recipe10
. - Navigate to the directory.
- Create the
main_read_output.go
file with the following content:
package main import ( "fmt" "os/exec" "runtime" ) func main() { var cmd string if runtime.GOOS == "windows" { cmd = "dir" } else { cmd = "ls" } proc := exec.Command(cmd) // Output will run the process // terminates and returns the standard // output in a byte slice. buff, err := proc.Output() if err != nil { panic(err) } // The output of child // process in form // of byte slice // printed as string fmt.Println(string(buff)) }
- Run the code by executing
go run main_read_output.go
. - See the output in the Terminal:

- Create the
main_read_stdout.go
file with the following content:
package main import ( "bytes" "fmt" "os/exec" "runtime" ) func main() { var cmd string if runtime.GOOS == "windows" { cmd = "dir" } else { cmd = "ls" } proc := exec.Command(cmd) buf := bytes.NewBuffer([]byte{}) // The buffer which implements // io.Writer interface is assigned to // Stdout of the process proc.Stdout = buf // To avoid race conditions // in this example. We wait till // the process exit. proc.Run() // The process writes the output to // to buffer and we use the bytes // to print the output. fmt.Println(string(buf.Bytes())) }
- Run the code by executing
go run main_read_stdout.go
. - See the output in the Terminal:

- Create the
main_read_read.go
file with the following content:
package main import ( "bufio" "context" "fmt" "os/exec" "time" ) func main() { cmd := "ping" timeout := 2 * time.Second // The command line tool // "ping" is executed for // 2 seconds ctx, _ := context.WithTimeout(context.TODO(), timeout) proc := exec.CommandContext(ctx, cmd, "example.com") // The process output is obtained // in form of io.ReadCloser. The underlying // implementation use the os.Pipe stdout, _ := proc.StdoutPipe() defer stdout.Close() // Start the process proc.Start() // For more comfortable reading the // bufio.Scanner is used. // The read call is blocking. s := bufio.NewScanner(stdout) for s.Scan() { fmt.Println(s.Text()) } }
- Run the code by executing
go run main_read.go
. - See the output in the Terminal:

- Create the
sample.go
file with the following content:
package main import ( "bufio" "fmt" "os" ) func main() { sc := bufio.NewScanner(os.Stdin) for sc.Scan() { fmt.Println(sc.Text()) } }
- Create the
main.go
file with the following content:
package main import ( "bufio" "fmt" "io" "os/exec" "time" ) func main() { cmd := []string{"go", "run", "sample.go"} // The command line tool // "ping" is executed for // 2 seconds proc := exec.Command(cmd[0], cmd[1], cmd[2]) // The process input is obtained // in form of io.WriteCloser. The underlying // implementation use the os.Pipe stdin, _ := proc.StdinPipe() defer stdin.Close() // For debugging purposes we watch the // output of the executed process stdout, _ := proc.StdoutPipe() defer stdout.Close() go func() { s := bufio.NewScanner(stdout) for s.Scan() { fmt.Println("Program says:" + s.Text()) } }() // Start the process proc.Start() // Now the following lines // are written to child // process standard input fmt.Println("Writing input") io.WriteString(stdin, "Hello\n") io.WriteString(stdin, "Golang\n") io.WriteString(stdin, "is awesome\n") time.Sleep(time.Second * 2) proc.Process.Kill() }
- Run the code by executing
go run main.go
. - See the output in the Terminal:

How it works…
The Cmd
structure of the os/exec
package provides the functions to access the output/input of the process. There are a few approaches to read the output of the process.
One of the simplest ways to read the process output is to use the Output
or CombinedOutput
method of the Cmd
structure (gets Stderr
and Stdout
). While calling this function, the program synchronously waits till the child process terminates and then returns the output to a byte buffer.
Besides the Output
and OutputCombined
methods, the Cmd
structure provides the Stdout
property, where the io.Writer
could be assigned. The assigned writer then serves as a destination for the process output. It could be a file, byte buffer or any type implementing the io.Writer
interface.
The last approach to read the process output is to get the io.Reader
from the Cmd
structure by calling the StdoutPipe
method. The StdoutPipe
method creates the pipe between the Stdout
, where the process writes the output, and provides Reader
which works as the interface for the program to read the process output. This way the output of the process is piped to the retrieved io.Reader
.
Writing to a process stdin
works the same way. Of all the options, the one with io.Writer
will be demonstrated.
As could be seen, there are a few ways to read and write from the child process. The use of stderr
and stdin
is almost the same as described in steps 6-7. Finally, the approach of how to access the input/output could be divided this way:
- Synchronous (wait until the process ends and get the bytes): The
Output
andCombinedOutput
methods ofCmd
are used. - IO: The output or input are provided in the form of
io.Writer/Reader
. TheXXXPipe
andStdXXX
properties are the right ones for this approach.
The IO type is more flexible and could also be used asynchronously.