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
- Syntax for Command Line
- Getting Started
- Generate Help
- Generate Version
- Error Handler
- Contributing
- License
go-cli is available using the standard go get command.
To install go-cli, simply run:
go get github.com/subchen/go-cli/v3// 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
A simple CLI application:
package main
import (
"fmt"
"os"
"github.com/subchen/go-cli/v3"
)
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 informationYou 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)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)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.
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.
-
base type:
*string*bool*int,*int8,*int16,*int32,*int64*uint,*uint8,*uint16,*uint32,*uint64*float32,*float64*time.Time,*time.Duration,*time.Location*net.IP,*net.IPMask,*net.IPNet*url.URL
-
slice of base type:
*[]string*[]int,*[]uint,*[]float64*[]net.IP,*[]net.IPNet*[]url.URL
-
cli.Value:
type Value interface { String() string Set(string) error }
Note: If you set
*boolasFlag.Value, theFlag.IsBoolwill be automaticallytrue.
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
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
&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",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
| Parsed Arguments | Resulting Value |
|---|---|
| --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 can be defined for a more git-like command line app.
package main
import (
"fmt"
"os"
"strings"
"github.com/subchen/go-cli/v3"
)
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(), ", "))
},
},
{
// alias name
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.
The default help flag (--help) is defined in cli.App and cli.Command.
All of the help text generation may be customized.
A help template is exposed as variable cli.HelpTemplate, that can be override.
// Append copyright
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)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/amd64You can rewrite version output using customized func.
app := cli.NewApp()
app.ShowVersion = func(app *App) {
fmt.Println("Version: ", app.Version)
}
app.Run(os.Args)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)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-cliwill only output error message without golang error stacks if app.OnActionPanic is nil.
- 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
Apache 2.0 license. See LICENSE