Richard Bucker

Command line parsing in Go

Posted at — Dec 22, 2014

There are a number of CLI libraries out there. Of course there is the stdlib, called flag, and then there are plenty of 3rd party versions. Cobra is an interesting manifestation because they have taken the controller approach as demonstrated in this article. The claim that the author makes is at least in two parts. [1] that the CLI behaves in a way consistent with the go tools. [2] supports nested commands.The challenge for the CLI is that there are so many opinions as to what is easy or best. The go tool and the docker CLI look a lot alike. And while the go authors have indicated that it is idiomatic to do as much as you can with the stdlib Cobra could be an exception.I decided to review the code in order to get a handle on the author and the implementation. If the author simply wrapped the flag package I would probably ignore the project and implement my own variation on the controller. The code seems nicely documented, however, one commonly argues that better function and attribute names can make all but the simplest comments meaningless. Also the comments do not seem lint approved. All of which makes the code harder to read.While the controller mechanism is a little smoke and mirrors… and cobra does come at a cost. One need only look at the go source in order to get a good example. Here is the code that the go tool CLI uses to parse the command line.  It’s only for _, cmd := range commands { if cmd.Name() == args[0] && cmd.Run != nil { cmd.Flag.Usage = func() { cmd.Usage() } if cmd.CustomFlags { args = args[1:] } else { cmd.Flag.Parse(args[1:]) args = cmd.Flag.Args() } cmd.Run(cmd, args) exit() return } }It’s not that cobra is bad but it’s the CLI. There is just no need to import a library for something you can control in a fraction of the code.UPDATE: Just as I was putting this idea to rest Gopher Academy posted another article and this time it made a little more sense than Cobra. The Viper toolkit takes the Cobra APIs to another level. This time the attributes can be assigned in various ways:setting defaultsreading from yaml, toml and json config filesreading from environment variablesreading from remote config systems (Etcd or Consul)reading from command line flagssetting explicit valuesThe only problem with this strategy is that it implies that the programmer has no idea what mechanism the devops team is going to use to deploy the application in the first place and this is a poor excuse for providing so many options.While I have been showing that Cobra and Viper are non essential packages I have an example of one that makes perfect sense. This is an article on git2go. It’s a package that gives you API access to the git and right in the sweet spot.UPDATE: I’ve been working on a go version of flock. There are a number of challenges with the flag package. The first is that flag aliases and flag groups. Aliases might be like: -c or -command or arg(N); and groups might be -s for shared and -x for exclusive. Flag does not have any algebra to support this sort of specification putting the burden on the programmer, however, these types of truth tables can get very complicated to design, implement and test.