本篇文章是 Go 標準庫 flag 包的快速上手篇。html
開發一個命令行工具,視複雜程度,通常要選擇一個合適的命令行解析庫,簡單的需求用 Go 標準庫 flag 就夠了,flag 的使用很是簡單。node
固然,除了標準庫 flag 外,也有很多的第三方庫。好比,爲了替代 flag 而生的 pflag,它支持 POSIX 風格的命令行解析。關於 POSIX 風格,本文末尾有個簡單的介紹。git
更多與命令行處理相關的庫,能夠打開 awesome-go#command-line 命令行一節查看,star 最多的是 spf13/cobra 和 urfave/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
方式定義新的 Flag
,flag.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
設置爲 false
和 version
設置爲 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 出現過不少不一樣的分支,命令行的風格也所以有不少標準,好比:
-
加一個字母,好比 -v
,短選項就是它,優勢是足夠簡潔;-
,沒有任何的前綴,不知道有參數的狀況怎麼處理,沒有研究;--
,如 --version
,長選項,擴展性好,可是要多打幾個字母;在網上找到一個搞笑漫畫。
查看系統進程有兩種寫法, ps aux
(BSD 風格) 和 ps -elf
(Unix 風格)。以前,我一直很鬱悶爲何有這個區別。如今算是明白了。哈哈。
POSIX 的命令行風格算是取長補短的集合吧。什麼是 POSIX 風格?能夠查看這篇文檔 命令參數語法。它同時提供了長短選項的標準。
要明白的是,標準終究只是標準,不少命令其實並不遵循它。但本身在設計命令行規範的時候,最好仍是要有一套標準,而參考最統一的標準確定是沒錯的。
本文介紹了 Go 中 flag 包的使用,通常的場景已經足夠使用了。最後,簡單地談了一個比較趣味性的話題,命令行的風格,是否有種感受,程序員之間的門派之爭真是無處不在。
歡迎關注個人微信公衆號。