Redirect stdout pipe of child process in Go

GoStdoutCommand Line-Interface

Go Problem Overview


I'm writing a program in Go that executes a server like program (also Go). Now I want to have the stdout of the child program in my terminal window where I started the parent program. One way to do this is with the cmd.Output() function, but this prints the stdout only after the process has exited. (That's a problem because this server-like program runs for a long time and I want to read the log output)

The variable out is of type io.ReadCloser and I don't know what I should do with it to achieve my task, and I can't find anything helpful on the web on this topic.

func main() {
	cmd := exec.Command("/path/to/my/child/program")
	out, err := cmd.StdoutPipe()
	if err != nil {
	    fmt.Println(err)
	}
	err = cmd.Start()
	if err != nil {
		fmt.Println(err)
	}
    //fmt.Println(out)
	cmd.Wait()
} 

Explanation to the code: uncomment the Println function to get the code to compile, I know that Println(out io.ReadCloser) is not a meaningful function.
(it produces the output &{3 |0 <nil> 0} ) These two lines are just required to get the code to compile.

Go Solutions


Solution 1 - Go

> Now I want to have the stdout of the child program in my terminal > window where I started the parent program.

No need to mess with pipes or goroutines, this one is easy.

func main() {
    // Replace `ls` (and its arguments) with something more interesting
    cmd := exec.Command("ls", "-l")
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    cmd.Run()
}

Solution 2 - Go

I believe that if you import io and os and replace this:

//fmt.Println(out)

with this:

go io.Copy(os.Stdout, out)

(see documentation for io.Copy and for os.Stdout), it will do what you want. (Disclaimer: not tested.)

By the way, you'll probably want to capture standard-error as well, by using the same approach as for standard-output, but with cmd.StderrPipe and os.Stderr.

Solution 3 - Go

For those who don't need this in a loop, but would like the command output to echo into the terminal without having cmd.Wait() blocking other statements:

package main

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

func checkError(err error) {
	if err != nil {
		log.Fatalf("Error: %s", err)
	}
}

func main() {
	// Replace `ls` (and its arguments) with something more interesting
	cmd := exec.Command("ls", "-l")

	// Create stdout, stderr streams of type io.Reader
	stdout, err := cmd.StdoutPipe()
	checkError(err)
	stderr, err := cmd.StderrPipe()
	checkError(err)

	// Start command
	err = cmd.Start()
	checkError(err)

	// Don't let main() exit before our command has finished running
	defer cmd.Wait()  // Doesn't block

	// Non-blockingly echo command output to terminal
	go io.Copy(os.Stdout, stdout)
	go io.Copy(os.Stderr, stderr)

	// I love Go's trivial concurrency :-D
	fmt.Printf("Do other stuff here! No need to wait.\n\n")
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionmbertView Question on Stackoverflow
Solution 1 - GocmccabeView Answer on Stackoverflow
Solution 2 - GoruakhView Answer on Stackoverflow
Solution 3 - GoelimisteveView Answer on Stackoverflow