Redirect stdout pipe of child process in Go
Solution 1
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
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
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")
}
Related videos on Youtube
mbert
Updated on October 13, 2020Comments
-
mbert over 3 years
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 oftype 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 thatPrintln(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.-
John Leimon about 11 yearsYour "exec" line of the import statement should be "os/exec".
-
mbert about 11 yearsthanks for the info, actually it was only exec pre go1, now its in os. updated it for go1
-
rmonjo almost 11 yearsI don't think that you actually need to call
io.Copy
within go routines -
weberc2 over 10 yearsI don't think you need to call
cmd.Wait()
or thefor{}
loop... why are these here? -
mbert over 10 years@weberc2 for this look down to elimisteve's answer. The for loop is not needed if you just want to run the program once. But if you don't call cmd.Wait(), your main() may end before your called program finishes, and you don't get the output you want
-
weberc2 over 10 years@mbert I don't think that's true. Consider this example in which "helper" is a program w/ a loop that prints a number every second.
io.Copy()
will block until the program finishes. play.golang.org/p/_I1MxATn90 Of course, @elimisteve's answer is more concise and generally better, but I still didn't need a for loop, goroutines, orcmd.Wait()
-
mbert over 10 yearsI just checked, io.Copy will block, but running go routines do not block main(). So after your main is finished it will take down your child program and copying goroutines. In this case cmd.Wait is required. (updated the solution to a shorter code)
-
AJcodez about 10 years@mbert you can throw away errors for quickly testing something with
out, _ := cmd...
-
kioopi almost 10 yearsIf you try to open
vim
this way and your terminal output is messed up afterwards: Add a line:cmd.Stdin = os.Stdin
before run.
-
-
ruakh over 12 years@mbert: I had used enough other languages, and had read enough about Go, to have a hunch for what feature would likely exist to do this, and in approximately what form; then I just had to look through the relevant package-docs (found by Googling) to confirm that my hunch was correct, and to find the necessary details. The hardest parts were (1) finding what standard-output is called (
os.Stdout
) and (2) confirming the premise that, if you don't callcmd.StdoutPipe()
at all, the standard-output goes to/dev/null
rather than to the parent-process's standard-output. -
galaktor over 10 yearsMinor fyi: (Obviously) you might miss results of the goroutines started if your "do other stuff here" completes faster than the goroutines. The main() exiting will cause the goroutines to end as well. so you could potentially not end up actually outputing to echo in the terminal if you don't wait for the cmd to finish.
-
Nucleon about 10 yearsIn addition, if you want the command to listen for input you can simply set
cmd.Stdin = os.Stdin
thereby making it as if you had literally executed that command from your shell. -
Rick Smith almost 10 yearsFor those looking to redirect to
log
instead of stdout, there is an answer here