一覧へ戻る
mkideal/cli(新しいタブで開きます)
主要言語: Go
CLI - A package for building command line app with go
- star 数
- 723
- watcher 数
- 21
- fork 数
- 42
- issue 数
- 4
- 最終更新
- 2026/09/06
主要言語: Go
CLI - A package for building command line app with go
hl => help, "veron" => "version").dft:"$HOME/dev").git commit command.(See example 21 and 22)See godoc
// main.go // This is a HelloWorld-like example package main import ( "os" "github.com/mkideal/cli" ) type argT struct { Name string `cli:"name" usage:"tell me your name"` } func main() { os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error { argv := ctx.Argv().(*argT) ctx.String("Hello, %s!\n", argv.Name) return nil })) }
$ go build -o hello
$ ./hello --name Clipher
Hello, Clipher!// main.go // This example show basic usage of flag package main import ( "os" "github.com/mkideal/cli" ) type argT struct { cli.Helper Port int `cli:"p,port" usage:"short and long format flags both are supported"` X bool `cli:"x" usage:"boolean type"` Y bool `cli:"y" usage:"boolean type, too"` } func main() { 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) return nil })) }
$ 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 flag package main import ( "os" "github.com/mkideal/cli" ) type argT struct { cli.Helper Id uint8 `cli:"*id" usage:"this is a required flag, note the *"` } func main() { os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error { argv := ctx.Argv().(*argT) ctx.String("%d\n", argv.Id) return nil })) }
$ go build -o app
$ ./app
ERR! required argument --id missing
$ ./app --id=2
2// main.go // This example show how to use default flag package main import ( "os" "github.com/mkideal/cli" ) type argT struct { cli.Helper Basic int `cli:"basic" usage:"basic usage of default" dft:"2"` Env string `cli:"env" usage:"env variable as default" dft:"$HOME"` Expr int `cli:"expr" usage:"expression as default" dft:"$BASE_PORT+1000"` DevDir string `cli:"devdir" usage:"directory of developer" dft:"$HOME/dev"` } func main() { 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) return nil })) }
$ 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 flag package main import ( "os" "github.com/mkideal/cli" ) type argT struct { // []bool, []int, []float32, ... supported too. Friends []string `cli:"F" usage:"my friends"` } func main() { os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error { ctx.JSONln(ctx.Argv()) return nil })) }
$ 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 flag package main import ( "os" "github.com/mkideal/cli" ) type argT struct { Macros map[string]int `cli:"D" usage:"define macros"` } func main() { os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error { ctx.JSONln(ctx.Argv()) return nil })) }
$ 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 true package main import ( "os" "github.com/mkideal/cli" ) type argT struct { Version bool `cli:"!v" usage:"force flag, note the !"` Required int `cli:"*r" usage:"required flag"` } func main() { os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error { argv := ctx.Argv().(*argT) if argv.Version { ctx.String("v0.0.1\n") } return nil })) }
$ 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 command package main import ( "fmt" "os" "github.com/mkideal/cli" ) func main() { if err := cli.Root(root, cli.Tree(help), cli.Tree(child), ).Run(os.Args[1:]); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } var help = cli.HelpCommand("display help information") // root command type rootT struct { cli.Helper Name string `cli:"name" usage:"your name"` } var root = &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 nil Argv: func() interface{} { return new(rootT) }, Fn: func(ctx *cli.Context) error { argv := ctx.Argv().(*rootT) ctx.String("Hello, root command, I am %s\n", argv.Name) return nil }, } // child command type childT struct { cli.Helper Name string `cli:"name" usage:"your name"` } var child = &cli.Command{ Name: "child", Desc: "this is a child command", Argv: func() interface{} { return new(childT) }, Fn: func(ctx *cli.Context) error { argv := ctx.Argv().(*childT) ctx.String("Hello, child command, I am %s\n", argv.Name) return nil }, }
$ 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.AutoHelper package main import ( "os" "github.com/mkideal/cli" ) type argT struct { Help bool `cli:"h,help" usage:"show help"` } // AutoHelp implements cli.AutoHelper interface // NOTE: cli.Helper is a predefined type which implements cli.AutoHelper func (argv *argT) AutoHelp() bool { return argv.Help } func main() { os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error { return nil })) }
$ 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 Validator package main import ( "fmt" "os" "github.com/mkideal/cli" ) type argT struct { cli.Helper Age int `cli:"age" usage:"your age"` Gender string `cli:"g,gender" usage:"your gender" dft:"male"` } // Validate implements cli.Validator interface func (argv *argT) Validate(ctx *cli.Context) error { if argv.Age < 0 || argv.Age > 300 { return fmt.Errorf("age %d out of range", argv.Age) } if argv.Gender != "male" && argv.Gender != "female" { return fmt.Errorf("invalid gender %s", ctx.Color().Yellow(argv.Gender)) } return nil } func main() { os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error { ctx.JSONln(ctx.Argv()) return nil })) }
$ 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 tag package main import ( "os" "github.com/mkideal/cli" ) type argT struct { cli.Helper Username string `cli:"u,username" usage:"github account" prompt:"type github account"` Password string `pw:"p,password" usage:"password of github account" prompt:"type the password"` } func main() { 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) return nil })) }
$ go build -o app $ ./app type github account: hahaha # visible type the password: # invisible because of `pw` tag username=hahaha, password=123456
// main.go // This example show how to use decoder package main import ( "os" "strings" "github.com/mkideal/cli" ) type exampleDecoder struct { list []string } // Decode implements cli.Decoder interface func (d *exampleDecoder) Decode(s string) error { d.list = strings.Split(s, ",") return nil } type argT struct { Example exampleDecoder `cli:"d" usage:"example decoder"` } func main() { os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error { argv := ctx.Argv().(*argT) ctx.JSONln(argv.Example.list) return nil })) }
$ go build -o app $ ./app -d a,b,c ["a","b","c"]
// main.go // This example show how to use builtin Decoder: PidFile package main import ( "os" "github.com/mkideal/cli" clix "github.com/mkideal/cli/ext" ) type argT struct { cli.Helper PidFile clix.PidFile `cli:"pid" usage:"pid file" dft:"013-pidfile.pid"` } func main() { os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error { argv := ctx.Argv().(*argT) if err := argv.PidFile.New(); err != nil { return err } defer argv.PidFile.Remove() return nil })) }
// main.go // This example show how to use builtin Decoder: Time and Duration package main import ( "os" "github.com/mkideal/cli" clix "github.com/mkideal/cli/ext" ) type argT struct { Time clix.Time `cli:"t" usage:"time"` Duration clix.Duration `cli:"d" usage:"duration"` } func main() { 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) return nil })) }
$ 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: File package main import ( "os" "github.com/mkideal/cli" clix "github.com/mkideal/cli/ext" ) type argT struct { Content clix.File `cli:"f,file" usage:"read content from file or stdin"` } func main() { os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error { argv := ctx.Argv().(*argT) ctx.String(argv.Content.String()) return nil })) }
$ 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 // * jsonfile package main import ( "os" "github.com/mkideal/cli" ) type config struct { A string B int C bool } type argT struct { JSON config `cli:"c,config" usage:"parse json string" parser:"json"` } func main() { os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error { argv := ctx.Argv().(*argT) ctx.JSONIndentln(argv.JSON, "", " ") return nil })) }
$ 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" ) type config struct { A string B int C bool } type argT struct { JSON config `cli:"c,config" usage:"parse json from file" parser:"jsonfile"` } func main() { os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error { argv := ctx.Argv().(*argT) ctx.JSONIndentln(argv.JSON, "", " ") return nil })) }
$ 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 parser package main import ( "os" "reflect" "github.com/mkideal/cli" ) type myParser struct { ptr interface{} } func newMyParser(ptr interface{}) cli.FlagParser { return &myParser{ptr} } // Parse implements FlagParser.Parse interface func (parser *myParser) Parse(s string) error { typ := reflect.TypeOf(parser.ptr) val := reflect.ValueOf(parser.ptr) if typ.Kind() == reflect.Ptr { kind := reflect.Indirect(val).Type().Kind() if kind == reflect.Struct { typElem, valElem := typ.Elem(), val.Elem() numField := valElem.NumField() for i := 0; i < numField; i++ { _, valField := typElem.Field(i), valElem.Field(i) if valField.Kind() == reflect.Int && valField.CanSet() { valField.SetInt(2) } if valField.Kind() == reflect.String && valField.CanSet() { valField.SetString("B") } } } } return nil } type config struct { A int B string } type argT struct { Cfg config `cli:"cfg" parser:"myparser"` } func main() { // register parser factory function cli.RegisterFlagParser("myparser", newMyParser) os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error { argv := ctx.Argv().(*argT) ctx.String("%v\n", argv.Cfg) return nil })) }
$ go build -o app
$ ./app
{0 }
$ ./app --cfg xxx
{2 B}// main.go // This example demonstrates how to use hooks package main import ( "fmt" "os" "github.com/mkideal/cli" ) func main() { if err := cli.Root(root, cli.Tree(child1), cli.Tree(child2), ).Run(os.Args[1:]); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } type argT struct { Error bool `cli:"e" usage:"return error"` } var root = &cli.Command{ Name: "app", Argv: func() interface{} { return new(argT) }, OnRootBefore: func(ctx *cli.Context) error { ctx.String("OnRootBefore invoked\n") return nil }, OnRootAfter: func(ctx *cli.Context) error { ctx.String("OnRootAfter invoked\n") return nil }, Fn: func(ctx *cli.Context) error { ctx.String("exec root command\n") argv := ctx.Argv().(*argT) if argv.Error { return fmt.Errorf("root command returns error") } return nil }, } var child1 = &cli.Command{ Name: "child1", Argv: func() interface{} { return new(argT) }, OnBefore: func(ctx *cli.Context) error { ctx.String("child1's OnBefore invoked\n") return nil }, OnAfter: func(ctx *cli.Context) error { ctx.String("child1's OnAfter invoked\n") return nil }, Fn: func(ctx *cli.Context) error { ctx.String("exec child1 command\n") argv := ctx.Argv().(*argT) if argv.Error { return fmt.Errorf("child1 command returns error") } return nil }, } var child2 = &cli.Command{ Name: "child2", NoHook: true, Fn: func(ctx *cli.Context) error { ctx.String("exec child2 command\n") return nil }, }
$ 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" ) type argT struct { cli.Helper Wait uint `cli:"wait" usage:"seconds for waiting" dft:"10"` Error bool `cli:"e" usage:"create an error"` } const successResponsePrefix = "start ok" func main() { if err := cli.Root(root, cli.Tree(daemon), ).Run(os.Args[1:]); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } var root = &cli.Command{ Argv: func() interface{} { return new(argT) }, Fn: func(ctx *cli.Context) error { argv := ctx.Argv().(*argT) if argv.Error { err := fmt.Errorf("occurs error") cli.DaemonResponse(err.Error()) return err } cli.DaemonResponse(successResponsePrefix) <-time.After(time.Duration(argv.Wait) * time.Second) return nil }, } var daemon = &cli.Command{ Name: "daemon", Argv: func() interface{} { return new(argT) }, Fn: func(ctx *cli.Context) error { return cli.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 commit package main import ( "os" "github.com/mkideal/cli" ) type argT struct { cli.Helper Msg string `edit:"m" usage:"message"` } func main() { os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error { argv := ctx.Argv().(*argT) ctx.String("msg: %s", argv.Msg) return nil })) }
$ 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" ) type argT struct { cli.Helper Msg string `edit:"m" usage:"message"` } func main() { cli.GetEditor = func() (string, error) { if editor := os.Getenv("EDITOR"); editor != "" { return editor, nil } return cli.DefaultEditor, nil } os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error { argv := ctx.Argv().(*argT) ctx.String("msg: %s", argv.Msg) return nil })) }
$ 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" ) type helloT struct { cli.Helper Name string `cli:"name" usage:"tell me your name" dft:"world"` Gender string `cli:"-"` // deprecated InternalUsage string `cli:"-"` // hide Age uint8 `cli:"a,age" usage:"tell me your age" dft:"100"` } func main() { 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) return nil })) }
$ 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