Go 命令行解析 flag 包之快速上手

本篇文章是 Go 標準庫 flag 包的快速上手篇。html

概述

開發一個命令行工具,視複雜程度,通常要選擇一個合適的命令行解析庫,簡單的需求用 Go 標準庫 flag 就夠了,flag 的使用很是簡單。node

固然,除了標準庫 flag 外,也有很多的第三方庫。好比,爲了替代 flag 而生的 pflag,它支持 POSIX 風格的命令行解析。關於 POSIX 風格,本文末尾有個簡單的介紹。git

更多與命令行處理相關的庫,能夠打開 awesome-go#command-line 命令行一節查看,star 最多的是 spf13/cobraurfave/cli ,與 flag / pflag 相比,它們更加複雜,是一個徹底的全功能的框架。程序員

有興趣均可以瞭解下。github

目標案例

迴歸主題,繼續介紹 flag 吧。經過案例介紹包的使用會比較直觀。bash

舉一個例子說明吧。假設,如今要開發一個 Go 語言環境的版本管理工具,gvg(go version management by go)。微信

命令行的幫助信息以下:框架

NAME:
   gvg - go version management by go

USAGE:
   gvg [global options] command [command options] [arguments...]

VERSION:
   0.0.1

COMMANDS:
   list       list go versions
   install    install a go version
   info       show go version info
   use        select a version
   uninstall  uninstall a go version
   get        get the latest code
   uninstall  uninstall a go version
   help, h    Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --help, -h     show help
   --version, -v  print the version
複製代碼

這個命令不只包含了全局的選項,還有 8 個子命令,部分子命令支持參數和選項。暫時,子命令的選項參數先不列出來了,實現時再看。工具

接下來,咱們試着經過 flag 實現這個效果。本文只介紹 GLOBAL OPTIONS(全局選項)的實現。ui

若是想了解什麼是 Go 語言環境的版本管理,能夠查看 如何靈活地進行 Go 版本管理 一文。

選項表示

最簡單的命令不須要任何參數和選項,複雜一點,要支持參數和選項的配置。gvg 沒有全局參數,或者說全局參數是子命令,全局選項有 --help -h--version -h

一個選項在 flag 包中用一個 Flag 表示,那 -h 能夠用一個 Flag 表示。一個選項一般由幾個部分組成,如名稱、使用說明和默認值。若是將 -h 用代碼表示,以下:

h := flag.Bool("h", false, "show help")
複製代碼

定義了一個布爾類型的 Flag,名爲 h,默認值是 false,使用說明爲 "show help"。變量 h 是一個布爾型的指針,經過它能夠取出命令行傳入的值。

除了使用 flag.Bool,還可使用另一種方式定義一個 Flag。咱們能夠用這種方式定義 -v 選項。

代碼以下:

var v bool
flag.BoolVar(&v, "v", false, "print the version")
複製代碼

最後的三個參數含義與 flag.Bool 相同,主要區別在值的獲取方式,flag.BoolVar 是經過將變量地址傳入獲取值。從經驗來看,第二種方式使用的較多,或許由於第一種方式會發生變量逃逸。

更多類型

除了布爾類型,Flag 的類型還有整數(int、int6四、uint、uint64)、浮點數(float64)、字符串(string)和時長(time.Duration)。

假設 gvg 的案例中,支持配置文件選項 --config-path。實現代碼以下:

var configPath

flag.StringVar(&configPath, "config-path", "", "config file path")
複製代碼

經過 StringVar 定義了新的 Flag。使用方式與 BoolVar 相同,最後的三個參數分別是選項名稱、默認值和使用說明。

雖然 flag 支持的內置類型並很少,但已經知足大部分需求了。若是有自定義的需求,也能夠擴展新的類型實現,這部份內容下篇介紹。

長短選項

如今已經完成了 -h-v 兩個選項,但目標是 -v --version-h --help,即同時支持長短選項。

一個 Flag 應該有長短兩種形式,但 flag 包並不支持這種風格,須要曲線救國才能實現。(注:本文開開頭提到的 pflag 支持。)

這裏以 -v --version 爲例,代碼以下:

flag.BoolVar(&v, "v", false, "print the version")
flag.BoolVar(&v, "version", false, "print the version")
複製代碼

定義了兩個 Flag,同時綁定到了一個變量上。這種效果只能用 flag.BoolVar 方式定義新的 Flagflag.Bool 沒辦法作到將同一個變量同時綁定兩個 Flag

但其實這種也有缺點,先不說了,後面介紹幫助信息打印時就明白了。

命令行解析

定義好全部 Flag,還須要一步解析才能拿到正確的結果。這一步很是簡單,調用 flag.Parse() 便可。

以下是完整的代碼:

package main

var h *bool
var v bool

func init() {
	flag.BoolVar(&h, "h", false, "show help")
	flag.BoolVar(&h, "help", false, "show help")
	flag.BoolVar(&v, "v", false, "print the version")
	flag.BoolVar(&v, "version", false, "print the version")
}

func main() {
	flag.Parse()
	fmt.Println("version", v)
	fmt.Println("help", h)
}
複製代碼

如今就能夠將它編譯爲 gvg 命令了。

使用命令

在正式使用命令前,先介紹下 flag 的語法。官方文檔說明,命令行中 flag 選項的使用語法有以下幾種形式。

-flag
-flag=x
-flag x // 非布爾類型才支持這種方式
複製代碼

但其實,-- 也是支持的。所以,上面才能夠實現 --version 的曲線救國。

使用下這個命令,將 help 設置爲 falseversion 設置爲 true。我儘可能把全部可能的寫法都列出來。

$ gvg -v
$ gvg -version -h=false  # 單個 - ,即 -version 支持
$ gvg --version=true --help=false
$ gvg --version=1 --help=0
$ gvg --version=t --help=f
$ gvg --version=T --help=F
$ gvg --version true --help true # 寫法錯誤,由於沒法識別出是 bool 值,仍是參數或子命令
$ gvg -vh  # 不支持這種風格
複製代碼

執行命令,輸出結果:

version true
help false
複製代碼

到這裏,flag 的快速入門就介紹完了。參數留在子命令的時候介紹。

命令行風格

因爲一些歷史緣由,Unix 出現過不少不一樣的分支,命令行的風格也所以有不少標準,好比:

  • Unix 風格,選項採用單 - 加一個字母,好比 -v,短選項就是它,優勢是足夠簡潔;
  • BSD 風格,選項沒有 -,沒有任何的前綴,不知道有參數的狀況怎麼處理,沒有研究;
  • GNU 風格,採用 --,如 --version,長選項,擴展性好,可是要多打幾個字母;

在網上找到一個搞笑漫畫。

查看系統進程有兩種寫法, ps aux(BSD 風格) 和 ps -elf(Unix 風格)。以前,我一直很鬱悶爲何有這個區別。如今算是明白了。哈哈。

POSIX 的命令行風格算是取長補短的集合吧。什麼是 POSIX 風格?能夠查看這篇文檔 命令參數語法。它同時提供了長短選項的標準。

要明白的是,標準終究只是標準,不少命令其實並不遵循它。但本身在設計命令行規範的時候,最好仍是要有一套標準,而參考最統一的標準確定是沒錯的。

總結

本文介紹了 Go 中 flag 包的使用,通常的場景已經足夠使用了。最後,簡單地談了一個比較趣味性的話題,命令行的風格,是否有種感受,程序員之間的門派之爭真是無處不在。


歡迎關注個人微信公衆號。

相關文章
相關標籤/搜索