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

封面.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 帮助文档

项目地址

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容

  • 官网 中文版本 好的网站 Content-type: text/htmlBASH Section: User ...
    不排版阅读 4,310评论 0 5
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,099评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,567评论 25 707
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,358评论 6 343
  • 我回忆,回忆这个暑假发生的一切;我拼命,拼命去记住发生的每一件温馨小事;因为这个暑假,对于我们而言,意义非凡。 回...
    reginali阅读 222评论 0 1