From d3bc797a7043aabe0543f16eed22595ff1a847ef Mon Sep 17 00:00:00 2001 From: Mark McGranaghan Date: Wed, 10 Oct 2012 11:52:34 -0700 Subject: [PATCH] spawning and execing processes --- examples.txt | 4 +- .../execing-processes/execing-processes.go | 42 ++++++----- .../execing-processes/execing-processes.sh | 6 +- .../spawning-processes/spawning-processes.go | 69 ++++++++++++++----- .../spawning-processes/spawning-processes.sh | 7 +- tools/generate.go | 1 + 6 files changed, 87 insertions(+), 42 deletions(-) diff --git a/examples.txt b/examples.txt index 441ee3c..a95e012 100644 --- a/examples.txt +++ b/examples.txt @@ -64,8 +64,8 @@ Line Filters # Command-Line Arguments # Command-Line Flags Environment Variables -# Spawning Processes -# Execing Processes +Spawning Processes +Exec'ing Processes # Signals # Exit # HTTP Client diff --git a/examples/execing-processes/execing-processes.go b/examples/execing-processes/execing-processes.go index c4e68da..453a9c8 100644 --- a/examples/execing-processes/execing-processes.go +++ b/examples/execing-processes/execing-processes.go @@ -1,11 +1,13 @@ -// In the previous example we looked at spawning external -// process. We do this when we need the functionality -// of another process accessable to a running Go process. -// In other cases we may just want to completely replace -// the current Go process with another process. To do -// this we'll use Go's implementation of the `exec`. +// In the previous example we looked at +// [spawning external processes](spawning-processes). We +// do this when we need an external process accessible to +// a running Go process. Sometimes we just want to +// completely replace the current Go process with another +// (perhaps non-Go) one. To do this we'll use Go's +// implementation of the classic +// exec +// function. -// In this example we'll exec an `ls` command. package main import "syscall" @@ -13,27 +15,31 @@ import "os" import "os/exec" func main() { - // We'll need an absolute path to the binary we'd - // like to execute. In this case we'll get the path - // for `ls`, probably `/bin/ls`. + + // For our example we'll exec `ls`. Go requires an + // abolute path to the binary we want to execute, so + // we'll use `exec.LookPath` to find it (probably + // `/bin/ls`). binary, lookErr := exec.LookPath("ls") if lookErr != nil { panic(lookErr) } - // Exec requires arguments in slice form (as - // apposed to one big string). Here we'll give `ls` - // a few arguments + // `Exec` requires arguments in slice form (as + // apposed to one big string). We'll give `ls` a few + // common arguments. args := []string{"-a", "-l", "-h"} - // We'll give the command we execute our current + // `Exec` also needs a set of [environment variables](environment-variables) + // to use. Here we just provide our current // environment. env := os.Environ() - // The actual exec call. If this call is succesful, - // the execution of our process will end here and it - // will be replaced by the `/bin/ls -a -l -h` process. - // If there is an error we'll get a return value. + // Here's the actual `os.Exec` call. If this call is + // succesful, the execution of our process will end + // here and be replaced by the `/bin/ls -a -l -h` + // process. If there is an error we'll get a return + // value. execErr := syscall.Exec(binary, args, env) if execErr != nil { panic(execErr) diff --git a/examples/execing-processes/execing-processes.sh b/examples/execing-processes/execing-processes.sh index 45807ab..70331ff 100644 --- a/examples/execing-processes/execing-processes.sh +++ b/examples/execing-processes/execing-processes.sh @@ -1,6 +1,4 @@ -# Now if we run this we'll see our programm replaced -# by `ls`. - +# When we run our program it is replaced by `ls`. $ go run execing-processes.go $ ls -a -l -h total 16 @@ -10,5 +8,5 @@ drwxr-xr-x 91 mark 3.0K Oct 3 12:50 .. # Note that Go does not offer a classic Unix `fork` # function. Usually this isn't an issue though, since -# starting goroutines, spawning processes, and execing +# starting goroutines, spawning processes, and exec'ing # processes covers most use cases for `fork`. diff --git a/examples/spawning-processes/spawning-processes.go b/examples/spawning-processes/spawning-processes.go index 17aefc0..43fa702 100644 --- a/examples/spawning-processes/spawning-processes.go +++ b/examples/spawning-processes/spawning-processes.go @@ -1,35 +1,70 @@ // Sometimes our Go programs need to spawn other, non-Go -// processes. For example, the syntax highlighting in this -// book is implementing by spawning a [`pygmentize`]() -// process from a Go program. Let's look at a few -// examples of spawning processes from 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 "os/exec" import "fmt" +import "io/ioutil" +import "os/exec" func main() { - // todo: explain + + // 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") - dateOut, dateErr := dateCmd.Output() - if dateErr != nil { - panic(dateErr) + + // `.Output` is another helper than handles the common + // case of running a comand, 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)) - // todo: piping in stdin + // Next we'll look at a slightly more involved case + // where we pipe data to the exteranl process on its + // `stdin` and collect the results from `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 ommited 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 explicit command and argument array, - // vs. being able to just pass in one command line. - // If you want to be able to just spawn a full - // command, you can use `bash`'s `-c` option: + // provide an explicitly deliniated 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, lsErr := lsCmd.Output() - if lsErr != nil { - panic(lsErr) + lsOut, err := lsCmd.Output() + if err != nil { + panic(err) } fmt.Println("> ls -a -l -h") fmt.Println(string(lsOut)) diff --git a/examples/spawning-processes/spawning-processes.sh b/examples/spawning-processes/spawning-processes.sh index 87fb2d9..8050788 100644 --- a/examples/spawning-processes/spawning-processes.sh +++ b/examples/spawning-processes/spawning-processes.sh @@ -1,6 +1,11 @@ +# The spawned programs return output that is the same +# as if we had run them directly form the comand-line. $ go run spawning-processes.go > date -Wed Oct 3 16:40:57 EDT 2012 +Wed Oct 10 09:53:11 PDT 2012 + +> grep hello +hello grep > ls -a -l -h drwxr-xr-x 4 mark 136B Oct 3 16:29 . diff --git a/tools/generate.go b/tools/generate.go index 7ad18c8..0454f3d 100644 --- a/tools/generate.go +++ b/tools/generate.go @@ -199,6 +199,7 @@ func parseExamples() []*Example { exampleId := strings.ToLower(exampleName) exampleId = strings.Replace(exampleId, " ", "-", -1) exampleId = strings.Replace(exampleId, "/", "-", -1) + exampleId = strings.Replace(exampleId, "'", "", -1) example.Id = exampleId example.Segs = make([][]*Seg, 0) sourcePaths := mustGlob("examples/" + exampleId + "/*")