Skip to content

Latest commit

History

260 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Command line interface

LicenseTravis branchCoverage StatusGo Report CardGoDoc

Screenshot

screenshot2

Key features

  • 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 commit command.(See example 21 and 22)

API documentation

See godoc

Examples

Example 1: Hello

back to examples

// 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!

Example 2: Flag

back to examples

// 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

Example 3: Required flag

back to examples

// 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

Example 4: Default flag

back to examples

// 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

Example 5: Slice

back to examples

// 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"]}

Example 6: Map

back to examples

// 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}}

Example 7: Force flag

back to examples

// 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

Example 8: Child command

back to examples

// 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?

Example 9: Auto help

back to examples

// 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 help

Try comment AutoHelp method and rerun it.

Example 10: Usage of Validator

back to examples

// 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"}

Example 11: Prompt and Password

back to examples

// 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

Example 12: Decoder

back to examples

// 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"]

Example 13: Pid file

back to examples

// 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
}))
}

Example 14: Time and Duration

back to examples

// 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

Example 15: File

back to examples

// 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

Example 16: Parser

back to examples

// 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
}

Example 17: JSON file

back to examples

// 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

Example 18: Custom parser

back to examples

// 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}

Example 19: Hooks

back to examples

// 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

Example 20: Daemon

back to examples

// 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

Example 21: Editor

back to examples

// 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

Example 22: Custom Editor

back to examples

// 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

Example 23: Hide flag

back to examples

// 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

About

CLI - A package for building command line app with go

Resources

Stars

722 stars

Watchers

21 watching

Forks

Releases

Packages

Used by

Contributors

Languages