// Sometimes our Go programs need to spawn other, non-Go // processes. package main import ( "fmt" "io" "os/exec" ) func main() { // We'll start with a simple command that takes no // arguments or input and just prints something to // stdout. The `exec.Command` helper creates an object // to represent this external process. dateCmd := exec.Command("date") // The `Output` method runs the command, waits for it // to finish and collects its standard output. // If there were no errors, `dateOut` will hold bytes // with the date info. dateOut, err := dateCmd.Output() if err != nil { panic(err) } fmt.Println("> date") fmt.Println(string(dateOut)) // `Output` and other methods of `Command` will return // `*exec.Error` if there was a problem executing the // command (e.g. wrong path), and `*exec.ExitError` // if the command ran but exited with a non-zero return // code. _, err = exec.Command("date", "-x").Output() if err != nil { switch e := err.(type) { case *exec.Error: fmt.Println("failed executing:", err) case *exec.ExitError: fmt.Println("command exit rc =", e.ExitCode()) default: panic(err) } } // Next we'll look at a slightly more involved case // where we pipe data to the external process on its // `stdin` and collect the results from its `stdout`. grepCmd := exec.Command("grep", "hello") // Here we explicitly grab input/output pipes, start // the process, write some input to it, read the // resulting output, and finally wait for the process // to exit. grepIn, _ := grepCmd.StdinPipe() grepOut, _ := grepCmd.StdoutPipe() grepCmd.Start() grepIn.Write([]byte("hello grep\ngoodbye grep")) grepIn.Close() grepBytes, _ := io.ReadAll(grepOut) grepCmd.Wait() // We omitted error checks in the above example, but // you could use the usual `if err != nil` pattern for // all of them. We also only collect the `StdoutPipe` // results, but you could collect the `StderrPipe` in // exactly the same way. fmt.Println("> grep hello") fmt.Println(string(grepBytes)) // Note that when spawning commands we need to // provide an explicitly delineated command and // argument array, vs. being able to just pass in one // command-line string. If you want to spawn a full // command with a string, you can use `bash`'s `-c` // option: lsCmd := exec.Command("bash", "-c", "ls -a -l -h") lsOut, err := lsCmd.Output() if err != nil { panic(err) } fmt.Println("> ls -a -l -h") fmt.Println(string(lsOut)) }