CLI, Directories, and Gob in Go

go_lang1Building on my last post, Playing with Go (AKA golang), I’m going to hack a command line interface (CLI), do some directory parsing, and add some code to read back the note I serialized to a file using gob. To read what was typed on the command line, import the “flag” package. In main(), you can then:

fmt.Println(“arguments: “, flag.Args())

I implemented a couple of useful functions that are missing from go: die, which prints an error message and exits with status 1, and assert, which tests a condition and, if false, dies with a message. They both use go’s bizarre “varargs” syntax, … interface{}, to allow me to pass a format string and an arbitrary list of arguments to a printf like formatter.

The code from the last post now implements the “new” command. I’ve implemented two additional commands, “list”, which lists all the notes dates in order, and “show”, which displays the body of the most recent note. Both the new commands use ioutil.ReadDir to get the list of the notes files in sorted order. The show command uses a gob decoder to read in and decode the note in the latest note file.

Here’s the complete program. Cut and paste it into your editor, save it, and go build it, then play with it. If you have any questions about it, feel free to post a comment.

package main

import "encoding/gob"
import "flag"
import "fmt"
import "io/ioutil"
import "os"
import "strings"
import "time"

type Note struct {
    Body []byte
    Created time.Time

func die(format string, args ...interface{}) {
    line := fmt.Sprintf(format, args...)
    if (!strings.HasSuffix(line, "\n")) {
        line += "\n"
    fmt.Fprint(os.Stderr, line)

func assert(condition bool, format string, args ...interface{}) {
    if (!condition) {
        die(format, args...)

func usageIf(condition bool) {
    if (condition) {
        die("usage: gnosis (new|list|show)")
func main() {
    args := flag.Args()
    usageIf(len(args) == 0)
    if (args[0] == "new") {
        var note Note
        note.Body, _ = ioutil.ReadAll(os.Stdin)
        note.Created = time.Now().UTC()

        os.Mkdir("notes", 0700)
        name := note.Created.Format("notes/2006-01-02T15:04:05.999999999.gob")
        file, err := os.OpenFile(name, os.O_CREATE | os.O_WRONLY, 0600)
        assert(err == nil, "Error '%v' creating file %v", err, name) 
        encoder := gob.NewEncoder(file)
        err = encoder.Encode(note)
        assert(err == nil, fmt.Sprintf("Error '%v' encoding note %v", err, note))
    if (args[0] == "list") {
        files, err := ioutil.ReadDir("notes")
        assert(err == nil, "Error '%v' reading directory 'notes'", err)

        for _, file := range files {
            stamp := file.Name()
            stamp = stamp[0:len(stamp) - 4] // Strip away .gob suffix
            fmt.Print(stamp + "\n")

    if (args[0] == "show") {
        files, err := ioutil.ReadDir("notes")
        assert(err == nil, "Error '%v' reading directory 'notes'", err)
        name := "notes/" + files[len(files) - 1].Name()
        file, err := os.Open(name)
        assert(err == nil, "Error '%v' opening file %v", err, name)
        decoder := gob.NewDecoder(file)
        var note Note
        err = decoder.Decode(&note)
        assert(err == nil, "Error '%v' decoding note", err)



About jimbelton

I'm a software developer, and a writer of both fiction and non-fiction, and I blog about movies, books, and philosophy. My interest in religious philosophy and the search for the truth inspires much of my writing.
This entry was posted in golang programming and tagged , , , , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s