- Lightweight and easy to use.
- Defines flag by tag, e.g. flag name(short or/and long), description, default value, password, prompt and so on.
- Type safety.
- Output looks very nice.
- Supports custom Validator.
- Supports slice and map as a flag.
- Supports any type as a flag field which implements cli.Decoder interface.
- Supports any type as a flag field which uses FlagParser.
- Suggestions for command.(e.g.
hl=>help, "veron" => "version"). - Supports default value for flag, even expression about env variable(e.g.
dft:"$HOME/dev"). - Supports editor like
git commitcommand.(See example 21 and 22)
See godoc
- Example 1: Hello world
- Example 2: How to use flag
- Example 3: How to use required flag
- Example 4: How to use default flag
- Example 5: How to use slice
- Example 6: How to use map
- Example 7: Usage of force flag
- Example 8: Usage of child command
- Example 9: Auto help
- Example 10: Usage of Validator
- Example 11: Prompt and Password
- Example 12: How to use Decoder
- Example 13: Builtin Decoder: PidFile
- Example 14: Builtin Decoder: Time and Duration
- Example 15: Builtin Decoder: File
- Example 16: Parser
- Example 17: Builtin Parser: JSONFileParser
- Example 18: How to use custom parser
- Example 19: How to use Hooks
- Example 20: How to use Daemon
- Example 21: How to use Editor
- Example 22: Custom Editor
- Example 23: How to hide/disable/deprecate flag
// main.go// This is a HelloWorld-like examplepackage main
import (
"os""github.com/mkideal/cli"
)
typeargTstruct {
Namestring`cli:"name" usage:"tell me your name"`
}
funcmain() {
os.Exit(cli.Run(new(argT), func(ctx*cli.Context) error {
argv:=ctx.Argv().(*argT)
ctx.String("Hello, %s!\n", argv.Name)
returnnil
}))
}$ go build -o hello
$ ./hello --name Clipher
Hello, Clipher!// main.go// This example show basic usage of flagpackage main
import (
"os""github.com/mkideal/cli"
)
typeargTstruct {
cli.HelperPortint`cli:"p,port" usage:"short and long format flags both are supported"`Xbool`cli:"x" usage:"boolean type"`Ybool`cli:"y" usage:"boolean type, too"`
}
funcmain() {
os.Exit(cli.Run(new(argT), func(ctx*cli.Context) error {
argv:=ctx.Argv().(*argT)
ctx.String("port=%d, x=%v, y=%v\n", argv.Port, argv.X, argv.Y)
returnnil
}))
}$ go build -o app
$ ./app -h
Options:
-h, --help display help information
-p, --port short and long format flags both are supported
-x boolean type
-y boolean type, too
$ ./app -p=8080 -x
port=8080, x=true, y=false
$ ./app -p 8080 -x=true
port=8080, x=true, y=false
$ ./app -p8080 -y true
port=8080, x=false, y=true
$ ./app --port=8080 -xy
port=8080, x=true, y=true
$ ./app --port 8080 -yx
port=8080, x=true, y=true// main.go// This example show how to use required flagpackage main
import (
"os""github.com/mkideal/cli"
)
typeargTstruct {
cli.HelperIduint8`cli:"*id" usage:"this is a required flag, note the *"`
}
funcmain() {
os.Exit(cli.Run(new(argT), func(ctx*cli.Context) error {
argv:=ctx.Argv().(*argT)
ctx.String("%d\n", argv.Id)
returnnil
}))
}$ go build -o app
$ ./app
ERR! required argument --id missing
$ ./app --id=2
2// main.go// This example show how to use default flagpackage main
import (
"os""github.com/mkideal/cli"
)
typeargTstruct {
cli.HelperBasicint`cli:"basic" usage:"basic usage of default" dft:"2"`Envstring`cli:"env" usage:"env variable as default" dft:"$HOME"`Exprint`cli:"expr" usage:"expression as default" dft:"$BASE_PORT+1000"`DevDirstring`cli:"devdir" usage:"directory of developer" dft:"$HOME/dev"`
}
funcmain() {
os.Exit(cli.Run(new(argT), func(ctx*cli.Context) error {
argv:=ctx.Argv().(*argT)
ctx.String("%d, %s, %d, %s\n", argv.Basic, argv.Env, argv.Expr, argv.DevDir)
returnnil
}))
}$ go build -o app
$ ./app -h
Options:
-h, --help display help information
--basic[=2] basic usage of default
--env[=$HOME] env variable as default
--expr[=$BASE_PORT+1000] expression as default
--devdir[=$HOME/dev] directory of developer
$ ./app
2, /Users/wang, 1000, /Users/wang/dev
$ BASE_PORT=8000 ./app --basic=3
3, /Users/wang, 9000, /Users/wang/dev// main.go// This example show how to use slice as a flagpackage main
import (
"os""github.com/mkideal/cli"
)
typeargTstruct {
// []bool, []int, []float32, ... supported too.Friends []string`cli:"F" usage:"my friends"`
}
funcmain() {
os.Exit(cli.Run(new(argT), func(ctx*cli.Context) error {
ctx.JSONln(ctx.Argv())
returnnil
}))
}$ go build -o app
$ ./app
{"Friends":null}
$ ./app -FAlice -FBob -F Charlie
{"Friends":["Alice","Bob","Charlie"]}// main.go// This example show how to use map as a flagpackage main
import (
"os""github.com/mkideal/cli"
)
typeargTstruct {
Macrosmap[string]int`cli:"D" usage:"define macros"`
}
funcmain() {
os.Exit(cli.Run(new(argT), func(ctx*cli.Context) error {
ctx.JSONln(ctx.Argv())
returnnil
}))
}$ go build -o app
$ ./app
{"Macros":null}
$ ./app -Dx=not-a-number
ERR!`not-a-number` couldn't converted to an int value$ ./app -Dx=1 -D y=2{"Macros":{"x":1,"y":2}}// main.go// This example show usage of force flag// Force flag has prefix !, and must be a boolean.// Will prevent validating flags if some force flag assigned truepackage main
import (
"os""github.com/mkideal/cli"
)
typeargTstruct {
Versionbool`cli:"!v" usage:"force flag, note the !"`Requiredint`cli:"*r" usage:"required flag"`
}
funcmain() {
os.Exit(cli.Run(new(argT), func(ctx*cli.Context) error {
argv:=ctx.Argv().(*argT)
ifargv.Version {
ctx.String("v0.0.1\n")
}
returnnil
}))
}$ go build -o app
$ ./app
ERR! required argument -r missing
# -v is a force flag, and assigned true, so `ERR` disappear.
$ ./app -v
v0.0.1// main.go// This example demonstrates usage of child commandpackage main
import (
"fmt""os""github.com/mkideal/cli"
)
funcmain() {
iferr:=cli.Root(root,
cli.Tree(help),
cli.Tree(child),
).Run(os.Args[1:]); err!=nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
varhelp=cli.HelpCommand("display help information")
// root commandtyperootTstruct {
cli.HelperNamestring`cli:"name" usage:"your name"`
}
varroot=&cli.Command{
Desc: "this is root command",
// Argv is a factory function of argument object// ctx.Argv() is if Command.Argv == nil or Command.Argv() is nilArgv: func() interface{} { returnnew(rootT) },
Fn: func(ctx*cli.Context) error {
argv:=ctx.Argv().(*rootT)
ctx.String("Hello, root command, I am %s\n", argv.Name)
returnnil
},
}
// child commandtypechildTstruct {
cli.HelperNamestring`cli:"name" usage:"your name"`
}
varchild=&cli.Command{
Name: "child",
Desc: "this is a child command",
Argv: func() interface{} { returnnew(childT) },
Fn: func(ctx*cli.Context) error {
argv:=ctx.Argv().(*childT)
ctx.String("Hello, child command, I am %s\n", argv.Name)
returnnil
},
}$ go build -o app
# help for root# equivalent to "./app -h"
$ ./app help
this is root command
Options:
-h, --help display help information
--name your name
Commands:
help display help information
child this is a child command# help for specific command# equivalent to "./app child -h"
$ ./app help child
this is a child command
Options:
-h, --help display help information
--name your name
# execute root command
$ ./app --name 123
Hello, root command, I am 123
# execute child command
$ ./app child --name=123
Hello, child command, I am 123
# something wrong, but got a suggestion.
$ ./app chd
ERR!command chd not found
Did you mean child?// main.go// This example demonstrates cli.AutoHelperpackage main
import (
"os""github.com/mkideal/cli"
)
typeargTstruct {
Helpbool`cli:"h,help" usage:"show help"`
}
// AutoHelp implements cli.AutoHelper interface// NOTE: cli.Helper is a predefined type which implements cli.AutoHelperfunc (argv*argT) AutoHelp() bool {
returnargv.Help
}
funcmain() {
os.Exit(cli.Run(new(argT), func(ctx*cli.Context) error {
returnnil
}))
}$ go build -o app
$ ./app -h
Options:
-h, --help show helpTry comment AutoHelp method and rerun it.
// main.go// This example demonstrates how to utilize Validatorpackage main
import (
"fmt""os""github.com/mkideal/cli"
)
typeargTstruct {
cli.HelperAgeint`cli:"age" usage:"your age"`Genderstring`cli:"g,gender" usage:"your gender" dft:"male"`
}
// Validate implements cli.Validator interfacefunc (argv*argT) Validate(ctx*cli.Context) error {
ifargv.Age<0||argv.Age>300 {
returnfmt.Errorf("age %d out of range", argv.Age)
}
ifargv.Gender!="male"&&argv.Gender!="female" {
returnfmt.Errorf("invalid gender %s", ctx.Color().Yellow(argv.Gender))
}
returnnil
}
funcmain() {
os.Exit(cli.Run(new(argT), func(ctx*cli.Context) error {
ctx.JSONln(ctx.Argv())
returnnil
}))
}$ go build -o app
$ ./app --age=-1
ERR! age -1 out of range
$ ./app --age=1000
ERR! age 1000 out of range
$ ./app -g balabala
ERR! invalid gender balabala
$ ./app --age 88 --gender female
{"Help":false,"Age":88,"Gender":"female"}// main.go// This example introduce prompt and pw tagpackage main
import (
"os""github.com/mkideal/cli"
)
typeargTstruct {
cli.HelperUsernamestring`cli:"u,username" usage:"github account" prompt:"type github account"`Passwordstring`pw:"p,password" usage:"password of github account" prompt:"type the password"`
}
funcmain() {
os.Exit(cli.Run(new(argT), func(ctx*cli.Context) error {
argv:=ctx.Argv().(*argT)
ctx.String("username=%s, password=%s\n", argv.Username, argv.Password)
returnnil
}))
}$ go build -o app
$ ./app
type github account: hahaha # visibletype the password: # invisible because of `pw` tag
username=hahaha, password=123456// main.go// This example show how to use decoderpackage main
import (
"os""strings""github.com/mkideal/cli"
)
typeexampleDecoderstruct {
list []string
}
// Decode implements cli.Decoder interfacefunc (d*exampleDecoder) Decode(sstring) error {
d.list=strings.Split(s, ",")
returnnil
}
typeargTstruct {
ExampleexampleDecoder`cli:"d" usage:"example decoder"`
}
funcmain() {
os.Exit(cli.Run(new(argT), func(ctx*cli.Context) error {
argv:=ctx.Argv().(*argT)
ctx.JSONln(argv.Example.list)
returnnil
}))
}$ go build -o app
$ ./app -d a,b,c
["a","b","c"]// main.go// This example show how to use builtin Decoder: PidFilepackage main
import (
"os""github.com/mkideal/cli"
clix "github.com/mkideal/cli/ext"
)
typeargTstruct {
cli.HelperPidFile clix.PidFile`cli:"pid" usage:"pid file" dft:"013-pidfile.pid"`
}
funcmain() {
os.Exit(cli.Run(new(argT), func(ctx*cli.Context) error {
argv:=ctx.Argv().(*argT)
iferr:=argv.PidFile.New(); err!=nil {
returnerr
}
deferargv.PidFile.Remove()
returnnil
}))
}// main.go// This example show how to use builtin Decoder: Time and Durationpackage main
import (
"os""github.com/mkideal/cli"
clix "github.com/mkideal/cli/ext"
)
typeargTstruct {
Time clix.Time`cli:"t" usage:"time"`Duration clix.Duration`cli:"d" usage:"duration"`
}
funcmain() {
os.Exit(cli.Run(new(argT), func(ctx*cli.Context) error {
argv:=ctx.Argv().(*argT)
ctx.String("time=%v, duration=%v\n", argv.Time, argv.Duration)
returnnil
}))
}$ go build -o app
$ ./app -t '2016-1-2 3:5' -d=10ms
time=2016-01-02 03:05:00 +0800 CST, duration=10ms// main.go// This example show how to use builtin Decoder: Filepackage main
import (
"os""github.com/mkideal/cli"
clix "github.com/mkideal/cli/ext"
)
typeargTstruct {
Content clix.File`cli:"f,file" usage:"read content from file or stdin"`
}
funcmain() {
os.Exit(cli.Run(new(argT), func(ctx*cli.Context) error {
argv:=ctx.Argv().(*argT)
ctx.String(argv.Content.String())
returnnil
}))
}$ go build -o app
# read from stdin
$ echo hello | ./app -f
hello
# read from file
$ echo hello > test.txt && ./app -f test.txt
hello
$ rm test.txt// main.go// This example introduce Parser// `Parser` is another way to use custom type of data.// Unlike `Decoder`, `Parser` used to parse string according to specific rule,// like json,yaml and so on.//// Builtin parsers:// * json// * jsonfilepackage main
import (
"os""github.com/mkideal/cli"
)
typeconfigstruct {
AstringBintCbool
}
typeargTstruct {
JSONconfig`cli:"c,config" usage:"parse json string" parser:"json"`
}
funcmain() {
os.Exit(cli.Run(new(argT), func(ctx*cli.Context) error {
argv:=ctx.Argv().(*argT)
ctx.JSONIndentln(argv.JSON, "", " ")
returnnil
}))
}$ go build -o app
$ ./app
{
"A": "",
"B": 0,
"C": false
}
$ ./app -c '{"A": "hello", "b": 22, "C": true}'
{
"A": "hello",
"B": 22,
"C": true
}// main.go// This example show how to use builtin parser: jsonfile// It's similar to json, but read string from file.package main
import (
"os""github.com/mkideal/cli"
)
typeconfigstruct {
AstringBintCbool
}
typeargTstruct {
JSONconfig`cli:"c,config" usage:"parse json from file" parser:"jsonfile"`
}
funcmain() {
os.Exit(cli.Run(new(argT), func(ctx*cli.Context) error {
argv:=ctx.Argv().(*argT)
ctx.JSONIndentln(argv.JSON, "", " ")
returnnil
}))
}$ go build -o app
$ echo'{"A": "hello", "b": 22, "C": true}'> test.json
$ ./app -c test.json
{
"A": "hello",
"B": 22,
"C": true
}
$ rm test.json// main.go// This example demonstrates how to use custom parserpackage main
import (
"os""reflect""github.com/mkideal/cli"
)
typemyParserstruct {
ptrinterface{}
}
funcnewMyParser(ptrinterface{}) cli.FlagParser {
return&myParser{ptr}
}
// Parse implements FlagParser.Parse interfacefunc (parser*myParser) Parse(sstring) error {
typ:=reflect.TypeOf(parser.ptr)
val:=reflect.ValueOf(parser.ptr)
iftyp.Kind() ==reflect.Ptr {
kind:=reflect.Indirect(val).Type().Kind()
ifkind==reflect.Struct {
typElem, valElem:=typ.Elem(), val.Elem()
numField:=valElem.NumField()
fori:=0; i<numField; i++ {
_, valField:=typElem.Field(i), valElem.Field(i)
ifvalField.Kind() ==reflect.Int&&valField.CanSet() {
valField.SetInt(2)
}
ifvalField.Kind() ==reflect.String&&valField.CanSet() {
valField.SetString("B")
}
}
}
}
returnnil
}
typeconfigstruct {
AintBstring
}
typeargTstruct {
Cfgconfig`cli:"cfg" parser:"myparser"`
}
funcmain() {
// register parser factory functioncli.RegisterFlagParser("myparser", newMyParser)
os.Exit(cli.Run(new(argT), func(ctx*cli.Context) error {
argv:=ctx.Argv().(*argT)
ctx.String("%v\n", argv.Cfg)
returnnil
}))
}$ go build -o app
$ ./app
{0 }
$ ./app --cfg xxx
{2 B}// main.go// This example demonstrates how to use hookspackage main
import (
"fmt""os""github.com/mkideal/cli"
)
funcmain() {
iferr:=cli.Root(root,
cli.Tree(child1),
cli.Tree(child2),
).Run(os.Args[1:]); err!=nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
typeargTstruct {
Errorbool`cli:"e" usage:"return error"`
}
varroot=&cli.Command{
Name: "app",
Argv: func() interface{} { returnnew(argT) },
OnRootBefore: func(ctx*cli.Context) error {
ctx.String("OnRootBefore invoked\n")
returnnil
},
OnRootAfter: func(ctx*cli.Context) error {
ctx.String("OnRootAfter invoked\n")
returnnil
},
Fn: func(ctx*cli.Context) error {
ctx.String("exec root command\n")
argv:=ctx.Argv().(*argT)
ifargv.Error {
returnfmt.Errorf("root command returns error")
}
returnnil
},
}
varchild1=&cli.Command{
Name: "child1",
Argv: func() interface{} { returnnew(argT) },
OnBefore: func(ctx*cli.Context) error {
ctx.String("child1's OnBefore invoked\n")
returnnil
},
OnAfter: func(ctx*cli.Context) error {
ctx.String("child1's OnAfter invoked\n")
returnnil
},
Fn: func(ctx*cli.Context) error {
ctx.String("exec child1 command\n")
argv:=ctx.Argv().(*argT)
ifargv.Error {
returnfmt.Errorf("child1 command returns error")
}
returnnil
},
}
varchild2=&cli.Command{
Name: "child2",
NoHook: true,
Fn: func(ctx*cli.Context) error {
ctx.String("exec child2 command\n")
returnnil
},
}$ go build -o app
# OnRootBefore => Fn => OnRootAfter
$ ./app
OnRootBefore invoked
exec root command
OnRootAfter invoked
# OnBefore => OnRootBefore => Fn => OnRootAfter => OnAfter
$ ./app child1
child1 OnBefore invoked
OnRootBefore invoked
exec child1 command
OnRootAfter invoked
child1 OnAfter invoked
# No hooks
$ ./app child2
exec child2 command# OnRootBefore => Fn --> Error
$ ./app -e
OnRootBefore invoked
exec root command
root command returns error
# OnBefore => OnRootBefore => Fn --> Error
$ ./app child1 -e
child1 OnBefore invoked
OnRootBefore invoked
exec child1 command
child1 command returns error// main.go// This example demonstrates how to use `Daemon`package main
import (
"fmt""os""time""github.com/mkideal/cli"
)
typeargTstruct {
cli.HelperWaituint`cli:"wait" usage:"seconds for waiting" dft:"10"`Errorbool`cli:"e" usage:"create an error"`
}
constsuccessResponsePrefix="start ok"funcmain() {
iferr:=cli.Root(root,
cli.Tree(daemon),
).Run(os.Args[1:]); err!=nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
varroot=&cli.Command{
Argv: func() interface{} { returnnew(argT) },
Fn: func(ctx*cli.Context) error {
argv:=ctx.Argv().(*argT)
ifargv.Error {
err:=fmt.Errorf("occurs error")
cli.DaemonResponse(err.Error())
returnerr
}
cli.DaemonResponse(successResponsePrefix)
<-time.After(time.Duration(argv.Wait) *time.Second)
returnnil
},
}
vardaemon=&cli.Command{
Name: "daemon",
Argv: func() interface{} { returnnew(argT) },
Fn: func(ctx*cli.Context) error {
returncli.Daemon(ctx, successResponsePrefix)
},
}$ go build -o daemon-app
$ ./daemone-app daemon
start ok
# Within 10 seconds, you will see process "./daemon-app"
$ ps | grep daemon-app
11913 ttys002 0:00.01 ./daemon-app
11915 ttys002 0:00.00 grep daemon-app
# After 10 seconds
$ ps | grep daemon-app
11936 ttys002 0:00.00 grep daemon-app
# try again with an error
$ ./daemon-app daemon -e
occurs error
$ ps | grep daemon-app
11936 ttys002 0:00.00 grep daemon-app// main.go// This example demonstrates how to use `editor`. This similar to git commitpackage main
import (
"os""github.com/mkideal/cli"
)
typeargTstruct {
cli.HelperMsgstring`edit:"m" usage:"message"`
}
funcmain() {
os.Exit(cli.Run(new(argT), func(ctx*cli.Context) error {
argv:=ctx.Argv().(*argT)
ctx.String("msg: %s", argv.Msg)
returnnil
}))
}$ go build -o app
$ ./app -m "hello, editor"
msg: hello, editor
$ ./app # Then, launch a editor(default is vim) and type `hello, editor`, quit the editor
msg: hello, editor// main.go// This example demonstrates specific editor.package main
import (
"os""github.com/mkideal/cli"
)
typeargTstruct {
cli.HelperMsgstring`edit:"m" usage:"message"`
}
funcmain() {
cli.GetEditor=func() (string, error) {
ifeditor:=os.Getenv("EDITOR"); editor!="" {
returneditor, nil
}
returncli.DefaultEditor, nil
}
os.Exit(cli.Run(new(argT), func(ctx*cli.Context) error {
argv:=ctx.Argv().(*argT)
ctx.String("msg: %s", argv.Msg)
returnnil
}))
}$ go build -o app
$ ./app -m "hello, editor"
msg: hello, editor
$ EDITOR=nano ./app # Then, launch nano and type `hello, editor`, quit the editor
msg: hello, editor// main.go// This example hides Gender and InternalUsage flags.package main
import (
"os""github.com/mkideal/cli"
)
typehelloTstruct {
cli.HelperNamestring`cli:"name" usage:"tell me your name" dft:"world"`Genderstring`cli:"-"`// deprecatedInternalUsagestring`cli:"-"`// hideAgeuint8`cli:"a,age" usage:"tell me your age" dft:"100"`
}
funcmain() {
os.Exit(cli.Run(new(helloT), func(ctx*cli.Context) error {
argv:=ctx.Argv().(*helloT)
ctx.String("Hello, %s! Your age is %d?\n", argv.Name, argv.Age)
returnnil
}))
}$ go build -o app
$ ./app -h
Options:
-h, --help display help information
--name[=world] tell me your name
-a, --age[=100] tell me your age