diff --git a/src/057-epochs/epochs.go b/src/057-epochs/epochs.go index d489859..a572b32 100644 --- a/src/057-epochs/epochs.go +++ b/src/057-epochs/epochs.go @@ -1,17 +1,21 @@ // ## Epochs +// A common requirement in programms is getting the number +// of seconds, milliseconds, or nanoseconds since the Unix +// epoch. Here's how to do it in Go. + package main import "time" func main() { - // Use `time.Now` with `Unix` or `UnixNane` to get + // Use `time.Now` with `Unix` or `UnixNano` to get // elapsed time since the Unix epoch. now := time.Now() secs := now.Unix() nanos := now.UnixNano() - // There is no `UnixMillis`. + // Note that there is no `UnixMillis`. millis := nanos / 1000000 println("Secs: ", secs) println("Millis:", millis) diff --git a/src/070-spawning-processes/spawning-processes.go b/src/070-spawning-processes/spawning-processes.go index 3fe1626..bfc7519 100644 --- a/src/070-spawning-processes/spawning-processes.go +++ b/src/070-spawning-processes/spawning-processes.go @@ -1,19 +1,38 @@ // ## Spawning Processes +// 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. + package main import "os/exec" import "fmt" func main() { - cmd := exec.Command("ls", "-a", "-l") - out, err := cmd.Output() - if err != nil { - panic(err) + // Explain + dateCmd := exec.Command("date") + dateOut, dateErr := dateCmd.Output() + if dateErr != nil { + panic(dateErr) } - fmt.Println("Files:") - fmt.Print(string(out)) -} + fmt.Println("> date") + fmt.Println(string(dateOut)) -// todo: full command lines with bash -// todo: piping in stdin + // todo: piping in stdin + + // 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: + lsCmd := exec.Command("bash", "-c", "ls -a -l -h") + lsOut, lsErr := lsCmd.Output() + if lsErr != nil { + panic(lsErr) + } + fmt.Println("> ls -a -l -h") + fmt.Println(string(lsOut)) +} diff --git a/src/070-spawning-processes/spawning-processes.sh b/src/070-spawning-processes/spawning-processes.sh index 62df9ce..87fb2d9 100644 --- a/src/070-spawning-processes/spawning-processes.sh +++ b/src/070-spawning-processes/spawning-processes.sh @@ -1 +1,8 @@ $ go run spawning-processes.go +> date +Wed Oct 3 16:40:57 EDT 2012 + +> ls -a -l -h +drwxr-xr-x 4 mark 136B Oct 3 16:29 . +drwxr-xr-x 91 mark 3.0K Oct 3 12:50 .. +-rw-r--r-- 1 mark 1.3K Oct 3 16:28 spawning-processes.go diff --git a/src/071-execing-processes/execing-processes.go b/src/071-execing-processes/execing-processes.go index 461d502..625cef0 100644 --- a/src/071-execing-processes/execing-processes.go +++ b/src/071-execing-processes/execing-processes.go @@ -1,5 +1,13 @@ // ## Exec'ing Processes +// In the previous chapter 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 this example we'll exec an `ls` command. package main import "syscall" @@ -7,16 +15,29 @@ 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`. 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 args := []string{"-a", "-l", "-h"} + + // We'll give the command we execute 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. execErr := syscall.Exec(binary, args, env) if execErr != nil { panic(execErr) } } - -// todo: note lack of fork diff --git a/src/071-execing-processes/execing-processes.sh b/src/071-execing-processes/execing-processes.sh new file mode 100644 index 0000000..45807ab --- /dev/null +++ b/src/071-execing-processes/execing-processes.sh @@ -0,0 +1,14 @@ +# Now if we run this we'll see our programm replaced +# by `ls`. + +$ go run execing-processes.go +$ ls -a -l -h +total 16 +drwxr-xr-x 4 mark 136B Oct 3 16:29 . +drwxr-xr-x 91 mark 3.0K Oct 3 12:50 .. +-rw-r--r-- 1 mark 1.3K Oct 3 16:28 execing-processes.go + +# 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 +# processes covers most use cases for `fork`. diff --git a/src/072-signals/signals b/src/072-signals/signals new file mode 100755 index 0000000..b44095e Binary files /dev/null and b/src/072-signals/signals differ diff --git a/src/072-signals/signals.go b/src/072-signals/signals.go index 7dfa0ac..d4a68b0 100644 --- a/src/072-signals/signals.go +++ b/src/072-signals/signals.go @@ -1,27 +1,42 @@ // ## Signals +// Sometines we'd like our Go programs to intelligently +// handle Unix signals. For example, we might want a +// server to gracefully shutdown when it receives a +// `SIGTERM`, or a command-line tool to stop processing +// input if it receives a `SIGINT`. Here's how to handle +// signals in Go with channels. + package main -import ( - "fmt" - "os" - "os/signal" - "syscall" -) +import "fmt" +import "os" +import "os/signal" +import "syscall" func main() { - c := make(chan os.Signal, 1) - d := make(chan bool, 1) + // Go signal notification works by sending `os.Signal` + // values on a channel. We'll create a channel to + // receive these notifications (we'll also make one to + // notify us when the program can exit.) + sigs := make(chan os.Signal, 1) + done := make(chan bool, 1) - signal.Notify(c, syscall.SIGINT) + // `signal.Notify` registers the given channel to + // receive notifications of the specified signals. + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) go func() { - sig := <-c + // This goroutine makes a blocking receive for + // signals. When it gets one it'll print it out + // and then notify the program that it can finish. + sig := <-sigs fmt.Println() fmt.Println(sig) - d <- true + done <- true }() - fmt.Println("Awaiting signal") - <-d + // The program will wait here until it gets the + // expected signal, and then exit. + fmt.Println("awaiting signal") + <- done + fmt.Println("exiting") } - -// todo: sending signals? diff --git a/src/072-signals/signals.sh b/src/072-signals/signals.sh index 3426418..9e5ff96 100644 --- a/src/072-signals/signals.sh +++ b/src/072-signals/signals.sh @@ -1,4 +1,9 @@ -$ go run signals.go -Awaiting signal +# When we run this program it will block waiting for a +# signal. By typing `ctrl-C` (which the +# terminal shows as `^C`) we cand send a `SIGNIT` signal, +# causing the program to print `interrupt` and then exit. +$ go run signals.go +awaiting signal ^C interrupt +exiting