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))
}
|