『如何构建命令行工具:YiYi』

发布于 2018-04-19 作者 超级苦工 397次 浏览 版块 分享

封面.png

大家好,我是谢伟,是一名程序员。

过去一阵子,我在开发一款客户端命令行工具,业余时间,开始写了下面这个工具。仅做学习参考使用。现在它看上去不够优雅,命令的命名也没有好好推敲。但功能都已实现。

即如何构建一个命令行工具,希望通过这个项目的示例,你能开发出各种各样符合你需求的命令行工具。

比如 github 上非常热门的命令行项目:

  • annie 下载视频和图片工具
  • hub 一个包装git 操作github 的工具
  • jfrog-cli-go 一个仓库管理平台的客户端

...

开始之前,还是看几个命令行工具一般是啥样的:

beego: 命令行工具bee


λ bee
Bee is a Fast and Flexible tool for managing your Beego Web Application.

USAGE
    bee command [arguments]

AVAILABLE COMMANDS

    version     Prints the current Bee version
    migrate     Runs database migrations
    api         Creates a Beego API application
    bale        Transforms non-Go files to Go source files
    fix         Fixes your application by making it compatible with newer versions of Beego
    dlv         Start a debugging session using Delve
    dockerize   Generates a Dockerfile for your Beego application
    generate    Source code generator
    hprose      Creates an RPC application based on Hprose and Beego frameworks
    new         Creates a Beego application
    pack        Compresses a Beego application into a single file
    rs          Run customized scripts
    run         Run the application by starting a local development server
    server      serving static content over HTTP on port

Use bee help [command] for more information about a command.

ADDITIONAL HELP TOPICS


Use bee help [topic] for more information about that topic.

思考一个问题:一个好的命令行应该具备什么?

  • 完善的帮助命令
  • 优雅的输出格式
  • 支持长、短参数
  • 命令补全
  • 支持表格输出

等等

实质:客户端命令行工具的实质是 接口或者API 的封装。

1、如何解析命令行参数

  • os.Args
  • Flag
  • cli

1. os.Args:

第一个参数是: 文件名称,之外的参数是命令行接收的参数

对参数进行处理即可实现解析命令行参数。

args = os.Args

oneArg = args[1]

TwoArg = args[2]

func add() int {
    number_one, _ := strconv.Atoi(args[2])
    number_two, _ := strconv.Atoi(args[3])
    return number_one + number_two
}

2. Flag

Flag golang 系统自带的库能更好的处理命令行参数:

    var operation string
    var numberone float64
    var numbertwo float64
    flag.StringVar(&operation, "o", "add", "operation for this tool")
    flag.Float64Var(&numberone, "n1", 0, "The first number")
    flag.Float64Var(&numbertwo, "n2", 0, "The second number")


定义三个参数,分别为string、float、float 类型

3. cli

这是一个第三方库,是一个命令参数解析的框架,能够很好的快速形成命令行。

cli项目地址

主要包括:

  • app 主要实现的是整体的命令行工具动作
  • command 对命令的处理
  • flag 对短参数的处理
  • help 使用 template 模板实现命令的帮助提示

2、如何组织项目

  • commands : 命令集合
  • domain: http 请求处理
  • main: 程序入口
  • objects: 定义结构体
  • reader : 命令清单入口
  • utils: 程序帮助程序入口

3、如何组织命令

第一级命令集合

func Commands() []cli.Command {
    return []cli.Command{
        {
            Name:        "book",
            Usage:       douban.BookUsage,
            Subcommands: douban.SubBookCommand(),
        },
        {
            Name:        "story",
            Usage:       one.StoryUsage,
            Subcommands: one.SubStoryCommand(),
        },
        {
            Name:        "movie",
            Usage:       douban.MovieUsage,
            Subcommands: douban.SubMovieCommand(),
        },
    }
}

第二级命令集合:

func SubBookCommand() []cli.Command {
    return []cli.Command{
        {
            Name:   "random",
            Action: actionBookNumber,
        },
        {
            Name:   "detail",
            Action: actionBookDetail,
        },
        {
            Name:        "search",
            Flags:       getFlagSearch(),
            Subcommands: subCommandBookSearch(),
        },
    }
}

第三级命令集合:

func subCommandBookSearch() []cli.Command {
    return []cli.Command{
        {
            Name:   "query",
            Action: actionQuery,
        },
        {
            Name:   "tag",
            Action: actionTag,
        },
    }
}

组合起来即是: YiYi.exe book random | detail | search query | search tag ...

可以看出该框架对命令的组织方式很优雅。可以让使用者聚焦在实现业务上。

4、操作步骤

YiYi.png
  • 组织命令
  • 实现具体函数处理

这里以:YiYi.exe book search query arg 这个命令讲述如何实现。

实例化APP


func main(){
    app := cli.NewApp()
    app.CommandNotFound = func(context *cli.Context, command string) {
        fmt.Printf("[[WARNING] Not Found Command: %s\n", command)
        fmt.Printf("[MESSAGE] Please Type: Reader --help")
    }
    app.Commands = reader.Commands()
    app.Run(os.Args)
}

定义Commands


func Commands() []cli.Command {
    return []cli.Command{
        {
            Name:        "book",
            Usage:       douban.BookUsage,
            Subcommands: douban.SubBookCommand(),
        },

    }
}

定义 SubCommand

func SubBookCommand() []cli.Command {
    return []cli.Command{

        {
            Name:        "search",
            Flags:       getFlagSearch(),
            Subcommands: subCommandBookSearch(),
        },
    }
}

func subCommandBookSearch() []cli.Command {
    return []cli.Command{
        {
            Name:   "query",
            Action: actionQuery,
        },
        {
            Name:   "tag",
            Action: actionTag,
        },
    }
}

实现 search query arg 命令


func actionQuery(c *cli.Context) {
    if c.NArg() == 1 {
        //fmt.Println(c.String("count"))
        url := fmt.Sprintf(bookSearchByQuery, c.Args().Get(0), strconv.Itoa(c.Int("count")))
        getBookSearch(url)
    }
}

具体实现:

结构体 和 模板输出

type BookAll struct {
    BookCollection []BookInfo
}
type BookInfo struct {
    Title    string
    Subtitle string
    URL      string
    Isbn10   string
    Isbn13   string
    Price    string
}

func (b BookAll) Template() {
    t := template.New("New Template for book")
    t, _ = t.Parse(`
Show Book from DouBan Api:
    AllCollections:
        {{range .BookCollection}}
        Title: {{.Title}}
        Subtitle: {{.Subtitle}}
        URL: {{.URL}}
        Isbn10: {{.Isbn10}}
        Isbn13: {{.Isbn13}}
        Price: {{.Price}}
        {{end}}
`)
    t.Execute(os.Stdout, b)
}

具体http 请求处理:

func getBookSearch(url string) {
    var allBook []objects.BookInfo
    data := utils.Response(url, "books")
    for _, one := range data.Array() {
        var oneBook objects.BookInfo
        oneBook.Title = one.Get("title").String()
        oneBook.Subtitle = one.Get("subtitle").String()
        oneBook.Isbn10 = one.Get("isbn10").String()
        oneBook.Isbn13 = one.Get("isbn13").String()
        oneBook.URL = one.Get("url").String()
        oneBook.Price = one.Get("price").String()
        allBook = append(allBook, oneBook)
    }
    AllData := objects.BookAll{
        BookCollection: allBook,
    }
    AllData.Template()
}

func Response(url string, key string) gjson.Result {
    var newClient httpclient.HttpClient
    newClient = httpclient.Implement{}
    resp, err := newClient.Get(url)
    //fmt.Println(url)
    if err != nil {
        fmt.Println("Get HTTP Response Failed")
        return gjson.Result{}
    }
    if key == "" {
        return gjson.Parse(string(resp))
    } else {
        return gjson.Parse(string(resp)).Get(key)
    }
}

gjson 处理 是用来处理 json 的第三方库,非常好用。

上面我们使用框架,一级一级实现下来,发现实际上我们只要聚焦实现业务:即http 请求 和 响应信息的处理即可。

5. 效果


YiYi.exe --help

YiYi is a tool for reading with DouBan and One APP api.                          
                                                                                 
                                                                                 
NAME:                                                                            
   YiYi - An application for book, movie, and story from DouBan and One App.     
                                                                                 
USAGE:                                                                           
   YiYi [global options] command [command options] [arguments...]                
                                                                                 
VERSION:                                                                         
                                                                                 
    ___       ___       ___       ___                                            
   /\__\     /\  \     /\__\     /\  \                                           
  |::L__L   _\:\  \   |::L__L   _\:\  \                                          
  |:::\__\ /\/::\__\  |:::\__\ /\/::\__\                                         
  /:;;/__/ \::/\/__/  /:;;/__/ \::/\/__/                                         
  \/__/     \:\__\    \/__/     \:\__\                                           
             \/__/               \/__/   v0.0.1                                  
                                                                                 
                                                                                 
DESCRIPTION:                                                                     
   An application for book, movie, and story from DouBan and One App.            
                                                                                 
AUTHOR:                                                                          
   xieWei <wuxiaoshen@shu.edu.cn>                                                
                                                                                 
COMMANDS:                                                                        
     book     get book info from DouBan API                                      
     story    get story info from One API                                        
     movie    get movie info from DouBan API                                      
     help, h  Shows a list of commands or help for one command                   
                                                                                 
GLOBAL OPTIONS:                                                                  
   --help, -h     show help                                                      
   --version, -v  print the version                                              


YiYi.exe book search query Golang


返回信息

Show Book from DouBan Api:
    AllCollections:

            Title: Cloud Native programming with Golang: Develop microservice-based high performance web apps for the cloud with Go
            Subtitle: Discover practical techniques to build cloud-native apps that are scalable, reliable, and always available.
            URL: https://api.douban.com/v2/book/30154908
            Isbn10: 178712598X
            Isbn13: 9781787125988
            Price: USD 44.99

            Title: Building RESTful Web services with Go: Learn how to build powerful RESTful APIs with Golang that scale gracefully
            Subtitle: Explore the necessary concepts of REST API development by building few real world services from scratch.
            URL: https://api.douban.com/v2/book/30154905
            Isbn10: 1788294289
            Isbn13: 9781788294287
            Price: USD 44.99

            Title: Go Standard Library Cookbook: Over 120 specific ways to make full use of the standard library components in Golang
            Subtitle: Implement solutions by leveraging the power of the GO standard library and reducing dependency on external crates
            URL: https://api.douban.com/v2/book/30179004
            Isbn10: 1788475275
            Isbn13: 9781788475273
            Price: USD 49.99

            Title: Go语言编程
            Subtitle:
            URL: https://api.douban.com/v2/book/11577300
            Isbn10: 7115290369
            Isbn13: 9787115290366
            Price: 49.00元

            Title: The Go Programming Language
            Subtitle:
            URL: https://api.douban.com/v2/book/26337545
            Isbn10: 0134190440
            Isbn13: 9780134190440
            Price: USD 39.99

            Title: Go Web编程
            Subtitle:
            URL: https://api.douban.com/v2/book/24316255
            Isbn10: 7121200910
            Isbn13: 9787121200915
            Price: 65.00元

            Title: Go语言学习笔记
            Subtitle:
            URL: https://api.douban.com/v2/book/26832468
            Isbn10: 7121291606
            Isbn13: 9787121291609
            Price: 89

            Title: Go 语言程序设计
            Subtitle:
            URL: https://api.douban.com/v2/book/24869910
            Isbn10: 7115317909
            Isbn13: 9787115317902
            Price: CNY 69.00

            Title: Go程序设计语言
            Subtitle:
            URL: https://api.douban.com/v2/book/27044219
            Isbn10: 7111558421
            Isbn13: 9787111558422
            Price: 79

            Title: Go并发编程实战
            Subtitle: Go并发编程实战
            URL: https://api.douban.com/v2/book/26244729
            Isbn10: 7115373981
            Isbn13: 9787115373984
            Price: 89元

            Title: Go in Action
            Subtitle:
            URL: https://api.douban.com/v2/book/25858023
            Isbn10: 1617291781
            Isbn13: 9781617291784
            Price: USD 39.99

具体用法:

YiYi 帮助文档

项目地址

收藏
暂无回复