cli - 构建强大命令行程序的工具箱 (2)

mkideal · · 3018 次点击 · 开始浏览    置顶
这是一个创建于 的主题,其中的信息可能已经有所发展或是发生改变。

获取 cli ======== cli 开源在 [github](https://github.com/mkideal/cli) 上,欢迎大家前去 star :-) 使用go get获取 ```shell go get github.com/mkideal/cli ``` [上一篇](http://studygolang.com/topics/1498)介绍了`cli` 是什么以及最简单的使用方法。本篇将开始一实例分析讲解`cli`的其他特性。 实例代码 ======= ```go 1 package main 2 3 import ( 4 "fmt" 5 "os" 6 7 "github.com/mkideal/cli" 8 ) 9 10 func main() { 11 app := &cli.Command{ 12 Name: os.Args[0], 13 Argv: func() interface{} { return new(argT) }, 14 Fn: func(ctx *cli.Context) error { 15 argv := ctx.Argv().(*argT) 16 if argv.Help { 17 ctx.WriteUsage() 18 } else { 19 ctx.String("argv=%v\n", *argv) 20 } 21 return nil 22 }, 23 } 24 25 app.RegisterFunc("help", func(ctx *cli.Context) error { 26 ctx.String("show help: sub commands: help/version\n") 27 return nil 28 }, nil) 29 30 app.Register(&cli.Command{ 31 // NOTE: Name is required, panic if "" 32 Name: "version", 33 34 // NOTE: Fn is required, panic if nil 35 Fn: func(ctx *cli.Context) error { 36 ctx.String("version: v0.0.1\n") 37 return nil 38 }, 39 40 // Argv is optional 41 42 Desc: "Desc represent command's abstract, optional", 43 Text: "Text represent command's detailed description, optional too", 44 }) 45 46 if err := app.Run(os.Args[1:]); err != nil { 47 fmt.Printf("%v\n", err) 48 } 49 } 50 51 type argT struct { 52 Help bool `cli:"h,help" usage:"show help"` 53 Host string `cli:"H,host" usage:"specify host address" dft:"127.0.0.1"` 54 Port uint16 `cli:"p,port" usage:"specify http port" dft:"8080"` 55 } ``` ### Command 对象 11~23行创建了一个名为`app`的Command。Command的公有属性如下: ```go // Command is the top-level instance in command-line app Command struct { Name string // Command name Desc string // Command abstract Text string // Command detailed description Fn CommandFunc // Command handler Argv ArgvFunc // Command argument factory function CanSubRoute bool HTTPRouters []string HTTPMethods []string ...... } ``` * `Name` - 命令的名字(对于非根命令,Name不能为空) * `Desc` - 命令的简要描述(可空,但建议填写) * `Text` - 命令的详细描述(可空) * `Fn` - 命令的功能函数 * `Argv` - 参数工厂函数(可空) * `CanSubRoute` - 是否允许子命令部分路由匹配 * `HTTPRouters` `HTTPMethods` - 到讲述HTTP的篇再讲 如13行所示 ```go 13 Argv: func() interface{} { return new(argT) }, ``` `Argv`是一个构建命令行参数对象的工厂函数,创建出来的对象将根据参数数组os.Args[1:]进行解析。 `CanSubRoute`的含义稍微复杂一点。如果在终端键入 ```shell ./app hello world -a --xyz=1 ``` 那么`./app`是会被理解为应用的名字 `hello` 被理解为应用程序的一个子命令 `world` 被理解为`hello`的一个子命令 当然我们程序里果真是有`hello`子命令,`hello`也果真有`world`子命令,那么一切OK。但是如果程序的`hello`子命令(假设有)没有`world`自命令,那么`CanSubRoute`就起作用了 * 此时如果`CanSubRoute`为true - 将执行`hello`,自`world`开始全是参数 * 反之,会报出命令未找到的错误 ### Context对象 `Context` 是命令执行时的上下文,它没有导出的共有属性,但提供几组公有方法。 ####参数相关的方法 ```go // Path 返回完整的命令路径 // 比如`./app hello world -a --xyz=1` 将返回 "hello world" // 当然如果 `hello` 子命令没有`world`子命令,而且`hello`自命令的CanSubRoute为true // 那么这里返回的将是"hello" func (ctx *Context) Path() string // Router 以数组格式返回完整的命令路径 // 比如`./app hello world -a --xyz=1` 将返回 ["hello" "world"] // 当然如果 `hello` 子命令没有`world`子命令,而且`hello`自命令的CanSubRoute为true // 那么这里返回的将是["hello"] func (ctx *Context) Router() []string // Args返回 除去命令外的原生参数 // 比如`./app hello world -a --xyz=1` 将返回 ["-a" "--xyz=1"] // 当然如果 `hello` 子命令没有`world`子命令,而且`hello`自命令的CanSubRoute为true // 那么这里返回的将是["world" "-a" "--xyz=1"] func (ctx *Context) Args() []string // Argv 返回解析所得的参数对象,如果Command的Argv为空,那么这里返回的就是空 func (ctx *Context) Argv() interface{} // FormValues 返回 url.Values 格式的命令行参数 func (ctx *Context) FormValues() url.Values ``` ####与命令相关的方法 ```go // Command 返回当前命令 func (ctx *Context) Command() *Command // Usage 返回当前命令的使用方法 func (ctx *Context) Usage() string // WriteUsage 输出当前命令的使用方法 func (ctx *Context) WriteUsage() ``` ####输出类方法 ```go // Writer 返回输出流对象 func (ctx *Context) Writer() io.Writer // Write 实现 io.Writer接口的Write方法 func (ctx *Context) Write(data []byte) (n int, err error) // Color 返回输出流使用的颜色对象 func (ctx *Context) Color() *color.Color // String 类似于fmt的Printf,只是输出到ctx的Writer里 func (ctx *Context) String(format string, args ...interface{}) *Context // JSON 将参数obj进行JSON编码然后写入Writer func (ctx *Context) JSON(obj interface{}) *Context // JSONln 类似于JSON方法,不过追加了一个换行 func (ctx *Context) JSONln(obj interface{}) *Context // JSONIndent 是美化的JSON方法 func (ctx *Context) JSONIndent(obj interface{}, prefix, indent string) *Context // JSONIndentln 类似于JSONIndent,不过追加了一个换行 func (ctx *Context) JSONIndentln(obj interface{}, prefix, indent string) *Context ``` ### 注册子命令 #### **RegisterFunc** 25~28行使用`Command`的`RegisterFunc`方法注册子命令。`RegisterFunc`包含3个入参 * name - 子命令名 * fn - 子命令的Fn * argvFn - 子命令的Argv ```go func (cmd *Command) RegisterFunc(name string, fn CommandFunc, argvFn ArgvFunc) *Command ``` #### **Register** 30~44行适应`Command`的`Register`方法注册子命令。`Register`方法以Command对象做为入参 ```go func (cmd *Command) Register(child *Command) *Command ``` ## 运行根命令 46行调用根命令`app`的`Run`函数运行程序。`Run`函数定义如下: ```go func (cmd *Command) Run(args []string) error { return cmd.RunWith(args, nil) } ``` 它调用了`RunWith`方法 ```go func (cmd *Command) RunWith(args []string, writer io.Writer, httpMethods ...string) error ``` ## 结语 今天将讲到这里了,今天讲述的栗子可参见[github](https://github.com/mkideal/cli/blob/master/examples/simple-command/main.go)

有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

3018 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传