A simple but powerful Go package to parse command-line arguments getopt(3) style. Designed especially for making CLI based libraries with ease. It has built-in support for sub-commands, long and short flag name combination (for example --version
<==> -v
), --flag=<value>
syntax, inverted flag (for example --no-clean
), variadic arguments, etc.
Commando CLI application builder library uses this package to parse command-line arguments.
$ go get "github.com/thatisuday/clapper"
// cmd.go
package main
import (
"fmt"
"os"
"github.com/thatisuday/clapper"
)
func main() {
// create a new registry
registry := clapper.NewRegistry()
// register the root command
if _, ok := os.LookupEnv("NO_ROOT"); !ok {
rootCommand, _ := registry.Register("") // root command
rootCommand.AddArg("output", "") //
rootCommand.AddFlag("force", "f", true, "") // --force, -f | default value: "false"
rootCommand.AddFlag("verbose", "v", true, "") // --verbose, -v | default value: "false"
rootCommand.AddFlag("version", "V", false, "") // --version, -V <value>
rootCommand.AddFlag("dir", "", false, "/var/users") // --dir <value> | default value: "/var/users"
}
// register the `info` sub-command
infoCommand, _ := registry.Register("info") // sub-command
infoCommand.AddArg("category", "manager") // default value: manager
infoCommand.AddArg("username", "") //
infoCommand.AddArg("subjects...", "") // variadic argument
infoCommand.AddFlag("verbose", "v", true, "") // --verbose, -v | default value: "false"
infoCommand.AddFlag("version", "V", false, "1.0.1") // --version, -V <value> | default value: "1.0.1"
infoCommand.AddFlag("output", "o", false, "./") // --output, -o <value> | default value: "./"
infoCommand.AddFlag("no-clean", "", true, "") // --no-clean | default value: "true"
// register the `ghost` sub-command
registry.Register("ghost")
/*----------------*/
// parse command-line arguments
command, err := registry.Parse(os.Args[1:])
/*----------------*/
// check for error
if err != nil {
fmt.Printf("error => %#v\n", err)
return
}
// get executed sub-command name
fmt.Printf("sub-command => %#v\n", command.Name)
// get argument values
for argName, argValue := range command.Args {
fmt.Printf("argument(%s) => %#v\n", argName, argValue)
}
// get flag values
for flagName, flagValue := range command.Flags {
fmt.Printf("flag(%s) => %#v\n", flagName, flagValue)
}
}
In the above example, we have registred a root command and an info
command. The registry
can parse arguments passed to the command that executed this program.
When the root command is executed with no command-line arguments.
$ go run cmd.go
sub-command => ""
argument(output) => &clapper.Arg{Name:"output", IsVariadic:false, DefaultValue:"", Value:""}
flag(dir) => &clapper.Flag{Name:"dir", ShortName:"", IsBoolean:false, IsInverted:false, DefaultValue:"/var/users", Value:""}
flag(force) => &clapper.Flag{Name:"force", ShortName:"f", IsBoolean:true, IsInverted:false, DefaultValue:"false", Value:""}
flag(verbose) => &clapper.Flag{Name:"verbose", ShortName:"v", IsBoolean:true, IsInverted:false, DefaultValue:"false", Value:""}
flag(version) => &clapper.Flag{Name:"version", ShortName:"V", IsBoolean:false, IsInverted:false, DefaultValue:"", Value:""}
When the root command is executed but not registered.
$ NO_ROOT=TRUE go run cmd.go
error => clapper.ErrorUnknownCommand{Name:""}
When the root command is executed with short/long flag names as well as by changing the positions of the arguments.
$ go run cmd.go userinfo -V 1.0.1 -v --force --dir ./sub/dir
$ go run cmd.go -V 1.0.1 --verbose --force userinfo --dir ./sub/dir
$ go run cmd.go -V 1.0.1 -v --force --dir ./sub/dir userinfo
$ go run cmd.go --version 1.0.1 --verbose --force --dir ./sub/dir userinfo
sub-command => ""
argument(output) => &clapper.Arg{Name:"output", IsVariadic:false, DefaultValue:"", Value:"userinfo"}
flag(verbose) => &clapper.Flag{Name:"verbose", ShortName:"v", IsBoolean:true, IsInverted:false, DefaultValue:"false", Value:"true"}
flag(version) => &clapper.Flag{Name:"version", ShortName:"V", IsBoolean:false, IsInverted:false, DefaultValue:"", Value:"1.0.1"}
flag(dir) => &clapper.Flag{Name:"dir", ShortName:"", IsBoolean:false, IsInverted:false, DefaultValue:"/var/users", Value:"./sub/dir"}
flag(force) => &clapper.Flag{Name:"force", ShortName:"f", IsBoolean:true, IsInverted:false, DefaultValue:"false", Value:"true"}
When an unregistered flag is provided in the command-line arguments.
$ go run cmd.go userinfo -V 1.0.1 -v --force -d ./sub/dir
error => clapper.ErrorUnknownFlag{Name:"-d"}
$ go run cmd.go userinfo -V 1.0.1 -v --force --d ./sub/dir
error => clapper.ErrorUnknownFlag{Name:"--d"}
$ go run cmd.go userinfo -V 1.0.1 -v --force --directory ./sub/dir
error => clapper.ErrorUnknownFlag{Name:"--directory"}
$ go run cmd.go info student --dump
error => clapper.ErrorUnknownFlag{Name:"--dump"}
$ go run cmd.go info student --clean
error => clapper.ErrorUnknownFlag{Name:"--clean"}
When information
was intended to be a sub-command but not registered and the root command accepts arguments.
$ go run cmd.go information --force
sub-command => ""
argument(output) => &clapper.Arg{Name:"output", IsVariadic:false, DefaultValue:"", Value:"information"}
flag(version) => &clapper.Flag{Name:"version", ShortName:"V", IsBoolean:false, IsInverted:false, DefaultValue:"", Value:""}
flag(dir) => &clapper.Flag{Name:"dir", ShortName:"", IsBoolean:false, IsInverted:false, DefaultValue:"/var/users", Value:""}
flag(force) => &clapper.Flag{Name:"force", ShortName:"f", IsBoolean:true, IsInverted:false, DefaultValue:"false", Value:"true"}
flag(verbose) => &clapper.Flag{Name:"verbose", ShortName:"v", IsBoolean:true, IsInverted:false, DefaultValue:"false", Value:""}
When a sub-command is executed.
$ go run cmd.go info student -V -v --output ./opt/dir
sub-command => "info"
argument(category) => &clapper.Arg{Name:"category", IsVariadic:false, DefaultValue:"manager", Value:"student"}
argument(username) => &clapper.Arg{Name:"username", IsVariadic:false, DefaultValue:"", Value:""}
argument(subjects) => &clapper.Arg{Name:"subjects", IsVariadic:true, DefaultValue:"", Value:""}
flag(verbose) => &clapper.Flag{Name:"verbose", ShortName:"v", IsBoolean:true, IsInverted:false, DefaultValue:"false", Value:"true"}
flag(version) => &clapper.Flag{Name:"version", ShortName:"V", IsBoolean:false, IsInverted:false, DefaultValue:"1.0.1", Value:""}
flag(output) => &clapper.Flag{Name:"output", ShortName:"o", IsBoolean:false, IsInverted:false, DefaultValue:"./", Value:"./opt/dir"}
flag(clean) => &clapper.Flag{Name:"clean", ShortName:"", IsBoolean:true, IsInverted:true, DefaultValue:"true", Value:""}
When a command is executed with an inverted flag (flag that starts with --no-
prefix).
$ go run cmd.go info student -V -v --output ./opt/dir --no-clean
sub-command => "info"
argument(username) => &clapper.Arg{Name:"username", IsVariadic:false, DefaultValue:"", Value:""}
argument(subjects) => &clapper.Arg{Name:"subjects", IsVariadic:true, DefaultValue:"", Value:""}
argument(category) => &clapper.Arg{Name:"category", IsVariadic:false, DefaultValue:"manager", Value:"student"}
flag(verbose) => &clapper.Flag{Name:"verbose", ShortName:"v", IsBoolean:true, IsInverted:false, DefaultValue:"false", Value:"true"}
flag(version) => &clapper.Flag{Name:"version", ShortName:"V", IsBoolean:false, IsInverted:false, DefaultValue:"1.0.1", Value:""}
flag(output) => &clapper.Flag{Name:"output", ShortName:"o", IsBoolean:false, IsInverted:false, DefaultValue:"./", Value:"./opt
When the position of argument values are changed and variadic arguments are provided.
$ go run cmd.go info -v student -V 2.0.0 thatisuday math science physics
$ go run cmd.go info student -v --version=2.0.0 thatisuday math science physics
$ go run cmd.go info student thatisuday math science -v physics -V=2.0.0
sub-command => "info"
argument(category) => &clapper.Arg{Name:"category", IsVariadic:false, DefaultValue:"manager", Value:"student"}
argument(username) => &clapper.Arg{Name:"username", IsVariadic:false, DefaultValue:"", Value:"thatisuday"}
argument(subjects) => &clapper.Arg{Name:"subjects", IsVariadic:true, DefaultValue:"", Value:"math,science,physics"}
flag(output) => &clapper.Flag{Name:"output", ShortName:"o", IsBoolean:false, IsInverted:false, DefaultValue:"./", Value:""}
flag(clean) => &clapper.Flag{Name:"clean", ShortName:"", IsBoolean:true, IsInverted:true, DefaultValue:"true", Value:""}
flag(verbose) => &clapper.Flag{Name:"verbose", ShortName:"v", IsBoolean:true, IsInverted:false, DefaultValue:"false", Value:"true"}
flag(version) => &clapper.Flag{Name:"version", ShortName:"V", IsBoolean:false, IsInverted:false, DefaultValue:"1.0.1", Value:"2.0.0"}
When a sub-command is registered without any flags.
$ go run cmd.go ghost -v thatisuday -V 2.0.0 teachers
error => clapper.ErrorUnknownFlag{Name:"-v"}
When a sub-command is registered without any arguments.
$ go run cmd.go ghost
$ go run cmd.go ghost thatisuday extra
sub-command => "ghost
When the root command is not registered or the root command is registered with no arguments.
$ NO_ROOT=TRUE go run cmd.go information
error => clapper.ErrorUnknownCommand{Name:"information"}
$ go run cmd.go ghost
sub-command => "ghost"
When unsupported flag format is provided.
$ go run cmd.go ---version
error => clapper.ErrorUnsupportedFlag{Name:"---version"}
$ go run cmd.go ---v=1.0.0
error => clapper.ErrorUnsupportedFlag{Name:"---v"}
$ go run cmd.go -version
error => clapper.ErrorUnsupportedFlag{Name:"-version"}
A lot of improvements can be made to this library, one of which is the support for combined short flags, like -abc
. If you are willing to contribute, create a pull request and mention your bug fixes or enhancements in the comment.