Sometimes our Go programs need to spawn other, non-Go processes. For example, the syntax highlighting on this site is [implemented](https://github.com/mmcgrana/gobyexample/blob/master/tools/generate.go) by spawning a [`pygmentize`](http://pygments.org/) process from a Go program. Let's look at a few examples of spawning processes from Go. | ||
package main |
||
import ( |
||
"fmt" |
||
"io/ioutil" |
||
"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") |
|
`.Output` is another helper that handles the common case of running a command, waiting for it to finish, and collecting its 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)) |
> date Wed Oct 21 16:47:51 UTC 2020 |
|
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, _ := ioutil.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)) |
Wed Oct 21 16:47:56 UTC 2020 > grep hello hello grep |
|
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)) |
Wed Oct 21 16:47:59 UTC 2020 > ls -a -l -h total 4.1M drwxrwxr-x 7 travis travis 4.0K Oct 21 16:46 . drwxrwxr-x 3 travis travis 4.0K Oct 21 16:45 .. drwxrwxr-x 2 travis travis 4.0K Oct 21 16:47 examples drwxrwxr-x 8 travis travis 4.0K Oct 21 16:45 .git -rw-rw-r-- 1 travis travis 33 Oct 21 16:45 .gitignore -rw-rw-r-- 1 travis travis 1.3K Oct 21 16:45 Makefile -rwxrwxr-x 1 travis travis 4.1M Oct 21 16:46 maketable -rw-rw-r-- 1 travis travis 1.8K Oct 21 16:45 README.md drwxrwxr-x 2 travis travis 4.0K Oct 21 16:45 site drwxrwxr-x 3 travis travis 4.0K Oct 21 16:46 tmp drwxrwxr-x 2 travis travis 4.0K Oct 21 16:45 tools -rw-rw-r-- 1 travis travis 106 Oct 21 16:45 .travis.yml |
|
} |