『如何構建命令行工具: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閱讀 228,739評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,634評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,653評論 0 377
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,063評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,835評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,235評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,315評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,459評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,000評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,819評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,004評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,560評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,257評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,676評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,937評論 1 288
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,717評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,003評論 2 374

推薦閱讀更多精彩內容

  • 官網 中文版本 好的網站 Content-type: text/htmlBASH Section: User ...
    不排版閱讀 4,424評論 0 5
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,786評論 18 139
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,666評論 25 708
  • Spring Boot 參考指南 介紹 轉載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,899評論 6 342
  • 我回憶,回憶這個暑假發生的一切;我拼命,拼命去記住發生的每一件溫馨小事;因為這個暑假,對于我們而言,意義非凡。 回...
    reginali閱讀 248評論 0 1