go-cli

go-cli is a package to build a CLI application. Support command/sub-commands.
Some applications are built using go-cli including:
Table of Contents
Installation
go-cli is available using the standard go get command.
To install go-cli, simply run:
go get github.com/subchen/go-cli
Syntax for Command Line
// Long option
--flag // boolean flags, or flags with no option default values
--flag x // only on flags without a default value
--flag=x
// Short option
-x // boolean flags
-x 123
-x=123
-x123 // value is 123
// value wrapped by quote
-x="123"
-x='123'
// unordered in flags and arguments
arg1 -x 123 arg2 --test arg3 arg4
// stops parsing after the terminator `--`
-x 123 -- arg1 --not-a-flag arg3 arg4
Getting Started
A simple CLI application:
package main
import (
"fmt"
"os"
"github.com/subchen/go-cli"
)
func main() {
app := cli.NewApp()
app.Name = "hello"
app.Version = "1.0.0"
app.Usage = "a hello world application."
app.Action = func(c *cli.Context) {
fmt.Println("Hello World!")
}
app.Run(os.Args)
}
Build and run our new CLI application
$ go build
$ ./hello
Hello World!
go-cli also generates neat help text
$ ./hello --help
NAME:
hello - a hello world application.
USAGE:
hello [options] [arguments...]
VERSION:
1.0.0
OPTIONS:
--help print this help
--version print version information
Arguments
You can lookup arguments by calling the Args function on cli.Context, e.g.:
app := cli.NewApp()
app.Action = func(c *cli.Context) {
name := c.Args()[0]
fmt.Printf("Hello %v\n", name)
}
app.Run(os.Args)
Flags
Setting and querying flags is simple.
app := cli.NewApp()
app.Flags = []*cli.Flag {
{
Name: "name",
Usage: "a name of user",
},
}
app.Action = func(c *cli.Context) {
name := c.GetString("name")
fmt.Printf("Hello %v\n", name)
}
app.Run(os.Args)
Bool flag
A bool flag can has a optional inline bool value.
&cli.Flag{
Name: "verbose",
Usage: "output verbose information",
IsBool: true,
},
The parsed arguments likes:
// valid
--verbose
--verbose=true
--verbose=false
// invalid
--verbose false
bool flag accepts 1,t,true,yes,on as true, 0,f,false,no,off as false.
Value bind
You can bind a variable for a Flag.Value, which will be set after parsed.
var name string
app := cli.NewApp()
app.Flags = []*cli.Flag {
{
Name: "name",
Usage: "a name of user",
Value: &name,
},
}
app.Action = func(c *cli.Context) {
fmt.Printf("Hello %v\n", name)
}
app.Run(os.Args)
Flag.Value can accept a cli.Value interface or a pointer of base type.
Note: If you set *bool as Flag.Value, the Flag.IsBool will be automatically true.
Short, Long, Alias Names
You can set multiply name in a flag, a short name, a long name, or multiple alias names.
&cli.Flag{
Name: "o, output, output-dir",
Usage: "A directory for output",
}
Then, results in help output like:
-o, --output, --output-dir value A directory for output
Placeholder
Sometimes it's useful to specify a flag's value within the usage string itself.
For example this:
&cli.Flag{
Name: "o, output",
Usage: "A directory for output",
Placeholder: "DIR",
}
Then, results in help output like:
-o DIR, --output DIR A directory for output
Default Value
&cli.Flag{
Name: "o, output",
Usage: "A directory for output",
DefValue: "/tmp/",
}
You also can set a default value got from the Environment
&cli.Flag{
Name: "o, output",
Usage: "A directory for output",
EnvVar: "APP_OUTPUT_DIR",
}
The EnvVar may also be given as a comma-delimited "cascade",
where the first environment variable that resolves is used as the default.
EnvVar: "APP_OUTPUT,APP_OUTPUT_DIR",
NoOptDefVal
If a flag has a NoOptDefVal and the flag is set on the command line without an option
the flag will be set to the NoOptDefVal.
For example given:
&cli.Flag{
Name: "flagname",
DefValue: "123",
NoOptDefVal: "456",
Value: &val
}
Would result in something like
| --flagname=000 | val=000 |
| --flagname | val=456 |
| [nothing] | val=123 |
Hidden flags
It is possible to mark a flag as hidden, meaning it will still function as normal,
however will not show up in usage/help text.
&cli.Flag{
Name: "secretFlag",
Hidden: true,
}
Commands
Commands can be defined for a more git-like command line app.
package main
import (
"fmt"
"os"
"strings"
"github.com/subchen/go-cli"
)
func main() {
app := cli.NewApp()
app.Name = "git"
app.Commands = []*cli.Command{
{
Name: "add",
Usage: "Add file contents to the index",
Action: func(c *cli.Context) {
fmt.Println("added files: ", strings.Join(c.Args(), ", "))
},
},
{
Name: "commit, co",
Usage: "Record changes to the repository",
Flags: []*cli.Flag {
{
Name: "m, message",
Usage: "commit message",
},
},
Hidden: false,
Action: func(c *cli.Context) {
fmt.Println("commit message: ", c.GetString("m"))
},
},
}
app.SeeAlso = `https://github.com/subchen/go-cli
https://github.com/subchen/go-cli/wiki`
app.Run(os.Args)
}
Also, you can use sub-commands in a command.
Generate Help
The default help flag (--help) is defined in cli.App and cli.Command.
Customize help
All of the help text generation may be customized.
A help template is exposed as variable cli.HelpTemplate, that can be override.
cli.HelpTemplate = cli.HelpTemplate + "@2017 Your company, Inc.\n\n"
Or, you can rewrite a help using customized func.
app := cli.NewApp()
app.ShowHelp = func(c *cli.HelpContext) {
fmt.Println("this is my help generated.")
}
app.Run(os.Args)
Generate Version
The default version flag (--version) is defined in cli.App.
app := cli.NewApp()
app.Name = "hello"
app.Version = "1.0.0"
app.BuildInfo = &cli.BuildInfo{
GitBranch: "master",
GitCommit: "320279c1a9a6537cdfd1e526063f6a748bb1fec3",
GitRevCount: "1234",
Timestamp: "Sat May 13 19:53:08 UTC 2017",
}
app.Run(os.Args)
Then, ./hello --version results like:
Name: hello
Version: 1.0.0
Patches: 1234
Git branch: master
Git commit: 320279c1a9a6537cdfd1e526063f6a748bb1fec3
Built: Sat May 13 19:53:08 UTC 2017
Go version: go1.8.1
OS/Arch: darwin/amd64
Customize version
You can rewrite version output using customized func.
app := cli.NewApp()
app.ShowVersion = func(app *App) {
fmt.Println("Version: ", app.Version)
}
app.Run(os.Args)
Error Handler
OnCommandNotFound
go-cli provides OnCommandNotFound func to handle an error if command/sub-command is not found.
app := cli.NewApp()
app.Flags = ...
app.Commands = ...
app.OnCommandNotFound = func(c *cli.Context, command string) {
c.ShowError(fmt.Errorf("Command not found: %s", command))
}
app.Run(os.Args)
OnActionPanic
go-cli provides OnActionPanic func to handle an error if panic in action.
app := cli.NewApp()
app.Flags = ...
app.Commands = ...
app.OnActionPanic = func(c *cli.Context, err error) {
os.Stderr.WriteString(fmt.Sprintf("fatal: %v\n", err))
}
app.Run(os.Args)
Notes: go-cli will only output error message without golang error stacks if app.OnActionPanic is nil.
Contributing
- Fork it
- Create your feature branch (git checkout -b my-new-feature)
- Commit your changes (git commit -am 'Add some feature')
- Push to the branch (git push origin my-new-feature)
- Create new Pull Request
License
Apache 2.0 license. See LICENSE