Go語言中命令行參數(shù)解析工具pflag的使用指南
在使用 Go 進(jìn)行開發(fā)的過程中,命令行參數(shù)解析是我們經(jīng)常遇到的需求。盡管 Go 標(biāo)準(zhǔn)庫提供了 flag 包用于實現(xiàn)命令行參數(shù)解析,但只能滿足基本需要,不支持高級特性。于是 Go 社區(qū)中出現(xiàn)了一個叫 pflag 的第三方包,功能更加全面且足夠強(qiáng)大。在本文中,我們將學(xué)習(xí)并掌握如何使用 pflag。
特點(diǎn)
pflag 作為 Go 內(nèi)置 flag 包的替代品,具有如下特點(diǎn):
- 實現(xiàn)了 POSIX/GNU 風(fēng)格的 --flags。
- pflag 與《The GNU C Library》 中「25.1.1 程序參數(shù)語法約定」章節(jié)中 POSIX 建議語法兼容。
- 兼容 Go 標(biāo)準(zhǔn)庫中的 flag 包。如果直接使用 flag 包定義的全局
FlagSet
對象CommandLine
,則完全兼容;否則當(dāng)你手動實例化了FlagSet
對象,這時就需要為每個標(biāo)志設(shè)置一個簡短標(biāo)志(Shorthand
)。
使用
基本用法
我們可以像使用 Go 標(biāo)準(zhǔn)庫中的 flag 包一樣使用 pflag。
package main import ( "fmt" "github.com/spf13/pflag" ) type host struct { value string } func (h *host) String() string { return h.value } func (h *host) Set(v string) error { h.value = v return nil } func (h *host) Type() string { return "host" } func main() { var ip *int = pflag.Int("ip", 1234, "help message for ip") var port int pflag.IntVar(&port, "port", 8080, "help message for port") var h host pflag.Var(&h, "host", "help message for host") // 解析命令行參數(shù) pflag.Parse() fmt.Printf("ip: %d\n", *ip) fmt.Printf("port: %d\n", port) fmt.Printf("host: %+v\n", h) fmt.Printf("NFlag: %v\n", pflag.NFlag()) // 返回已設(shè)置的命令行標(biāo)志個數(shù) fmt.Printf("NArg: %v\n", pflag.NArg()) // 返回處理完標(biāo)志后剩余的參數(shù)個數(shù) fmt.Printf("Args: %v\n", pflag.Args()) // 返回處理完標(biāo)志后剩余的參數(shù)列表 fmt.Printf("Arg(1): %v\n", pflag.Arg(1)) // 返回處理完標(biāo)志后剩余的參數(shù)列表中第 i 項 }
以上示例演示的 pflag 用法跟 flag 包用法一致,可以做到二者無縫替換。
示例分別使用 pflag.Int()
、pflag.IntVar()
、pflag.Var()
三種不同方式來聲明標(biāo)志。其中 ip
和 port
都是 int
類型標(biāo)志,host
標(biāo)志則為自定義的 host
類型,它實現(xiàn)了 pflag.Value
接口,通過實現(xiàn)接口類型,標(biāo)志能夠支持任意類型,增加靈活性。
通過 --help/-h
參數(shù)查看命令行程序使用幫助:
$ go run main.go --help Usage of ./main: --host host help message for host --ip int help message for ip (default 1234) --port int help message for port (default 8080) pflag: help requested
可以發(fā)現(xiàn),幫助信息中的標(biāo)志位置是經(jīng)過重新排序的,并不是標(biāo)志定義的順序。
與 flag 包不同的是,pflag 包參數(shù)定界符是兩個 -
,而不是一個 -
,在 pflag 中 --
和 -
具有不同含義,這點(diǎn)稍后會進(jìn)行介紹。
ip
標(biāo)志的默認(rèn)參數(shù)為 1234
,port
標(biāo)志的默認(rèn)參數(shù)為 8080
。
注意:在有些終端下執(zhí)行程序退出后,還會多打印一行 exit status 2
,這并不意味著程序沒有正常退出,而是因為 --help
意圖就是用來查看使用幫助,所以程序在打印使用幫助信息后,主動調(diào)用 os.Exit(2)
退出了。
通過如下方式使用命令行程序:
$ go run main.go --ip 1 x y --host localhost a b ip: 1 port: 8080 host: {value:localhost} NFlag: 2 NArg: 4 Args: [x y a b] Arg(1): y
ip
標(biāo)志的默認(rèn)值已被命令行參數(shù) 1
所覆蓋,由于沒有傳遞 port
標(biāo)志,所以打印結(jié)果為默認(rèn)值 8080
,host
標(biāo)志的值也能夠被正常打印。
還有 4 個非選項參數(shù)數(shù) x
、y
、a
、b
也都被 pflag 識別并記錄了下來。這點(diǎn)比 flag 要強(qiáng)大,在 flag 包中,非選項參數(shù)數(shù)只能寫在所有命令行參數(shù)最后,x
、y
出現(xiàn)在這里程序是會報錯的。
進(jìn)階用法
除了像 flag 包一樣的用法,pflag 還支持一些獨(dú)有的用法,以下是用法示例。
package main import ( "fmt" "os" "github.com/spf13/pflag" ) type host struct { value string } func (h *host) String() string { return h.value } func (h *host) Set(v string) error { h.value = v return nil } func (h *host) Type() string { return "host" } func main() { flagset := pflag.NewFlagSet("test", pflag.ExitOnError) var ip = flagset.IntP("ip", "i", 1234, "help message for ip") var boolVar bool flagset.BoolVarP(&boolVar, "boolVar", "b", true, "help message for boolVar") var h host flagset.VarP(&h, "host", "H", "help message for host") flagset.SortFlags = false flagset.Parse(os.Args[1:]) fmt.Printf("ip: %d\n", *ip) fmt.Printf("boolVar: %t\n", boolVar) fmt.Printf("host: %+v\n", h) i, err := flagset.GetInt("ip") fmt.Printf("i: %d, err: %v\n", i, err) }
首先我們通過 pflag.NewFlagSet
自定義了 FlagSet
對象 flagset
,之后的標(biāo)志定義和解析都通過 flagset
來完成。
前文示例中 pflag.Int()
這種用法,實際上使用的是全局 FlagSet
對象 CommandLine
,CommandLine
定義如下:
var CommandLine = NewFlagSet(os.Args[0], ExitOnError)
現(xiàn)在同樣使用三種不同方式來聲明標(biāo)志,分別為 flagset.IntP()
、flagset.BoolVarP()
、flagset.VarP()
。不難發(fā)現(xiàn),這三個方法的命名結(jié)尾都多了一個 P
,它們的能力也得以升級,三個方法都多了一個 shorthand string
參數(shù)(flagset.IntP
的第 2 個參數(shù),flagset.BoolVarP
和 flagset.VarP
的第 3 個參數(shù))用來設(shè)置簡短標(biāo)志。
從聲明標(biāo)志的方法名中我們能夠總結(jié)出一些規(guī)律:
pflag.<Type>
類方法名會將標(biāo)志參數(shù)值存儲在指針中并返回。pflag.<Type>Var
類方法名中包含Var
關(guān)鍵字的,會將標(biāo)志參數(shù)值綁定到第一個指針類型的參數(shù)。pflag.<Type>P
、pflag.<Type>VarP
類方法名以P
結(jié)尾的,支持簡短標(biāo)志。
一個完整標(biāo)志在命令行傳參時使用的分界符為 --
,而一個簡短標(biāo)志的分界符則為 -
。
flagset.SortFlags = false
作用是禁止打印幫助信息時對標(biāo)志進(jìn)行重排序。
示例最后,使用 flagset.GetInt()
獲取參數(shù)的值。
通過 --help/-h
參數(shù)查看命令行程序使用幫助:
$ go run main.go --help Usage of test: -i, --ip int help message for ip (default 1234) -b, --boolVar help message for boolVar (default true) -H, --host host help message for host pflag: help requested
這次的幫助信息中,標(biāo)志順序沒有被改變,就是聲明的順序。
每一個標(biāo)志都會對應(yīng)一個簡短標(biāo)志,如 -b
和 --boolVar
是等價的,可以更加方便的設(shè)置參數(shù)。
指定如下命令行參數(shù)運(yùn)行示例:
$ go run main.go --ip 1 -H localhost --boolVar=false ip: 1 boolVar: false host: {value:localhost} i: 1, err: <nil>
通過 --ip 1
使用完整標(biāo)志指定 ip
參數(shù)值。
通過 -H localhost
使用簡短標(biāo)志指定 host
參數(shù)值。
布爾類型的標(biāo)志指定參數(shù) --boolVar=false
需要使用等號 =
而非空格。
命令行標(biāo)志語法
命令行標(biāo)志遵循如下語法:
語法 | 說明 |
---|---|
--flag | 適用于 bool 類型標(biāo)志,或具有 NoOptDefVal 屬性的標(biāo)志。 |
--flag x | 適用于非 bool 類型標(biāo)志,或沒有 NoOptDefVal 屬性的標(biāo)志。 |
--flag=x | 適用于 bool 類型標(biāo)志。 |
-n 1234/-n=1234/-n1234 | 簡短標(biāo)志,非 bool 類型且沒有 NoOptDefVal 屬性,三者等價。 |
標(biāo)志解析在終止符 --
之后停止。
整數(shù)標(biāo)志接受 1234、0664、0x1234,并且可能為負(fù)數(shù)。
布爾標(biāo)志接受 1, 0, t, f, true, false, TRUE, FALSE, True, False。
Duration
標(biāo)志接受任何對 time.ParseDuration
有效的輸入。
標(biāo)志名 Normalize
借助 pflag.NormalizedName
我們能夠給標(biāo)志起一個或多個別名、規(guī)范化標(biāo)志名等。
package main import ( "fmt" "os" "strings" "github.com/spf13/pflag" ) func normalizeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName { // alias switch name { case "old-flag-name": name = "new-flag-name" break } // --my-flag == --my_flag == --my.flag from := []string{"-", "_"} to := "." for _, sep := range from { name = strings.Replace(name, sep, to, -1) } return pflag.NormalizedName(name) } func main() { flagset := pflag.NewFlagSet("test", pflag.ExitOnError) var ip = flagset.IntP("new-flag-name", "i", 1234, "help message for new-flag-name") var myFlag = flagset.IntP("my-flag", "m", 1234, "help message for my-flag") flagset.SetNormalizeFunc(normalizeFunc) flagset.Parse(os.Args[1:]) fmt.Printf("ip: %d\n", *ip) fmt.Printf("myFlag: %d\n", *myFlag) }
要使用 pflag.NormalizedName
,我們需要創(chuàng)建一個函數(shù) normalizeFunc
,然后將其通過 flagset.SetNormalizeFunc(normalizeFunc)
注入到 flagset
使其生效。
在 normalizeFunc
函數(shù)中,我們給 new-flag-name
標(biāo)志起了一個別名 old-flag-name
。
另外,還對標(biāo)志名進(jìn)行了規(guī)范化處理,帶有 -
和 _
分割符的標(biāo)志名,會統(tǒng)一規(guī)范化成以 .
作為分隔符的標(biāo)志名。
使用示例如下:
$ go run pflag.go --old-flag-name 2 --my-flag 200 ip: 2 myFlag: 200 $ go run pflag.go --new-flag-name 3 --my_flag 300 ip: 3 myFlag: 300
NoOptDefVal
NoOptDefVal
是 no option default values
的簡寫。
創(chuàng)建標(biāo)志后,可以為標(biāo)志設(shè)置 NoOptDefVal
屬性,如果標(biāo)志具有 NoOptDefVal
屬性并且在命令行上設(shè)置了標(biāo)志而沒有參數(shù)選項,則標(biāo)志將設(shè)置為 NoOptDefVal
指定的值。
如下示例:
var ip = flag.IntP("flagname", "f", 1234, "help message") flag.Lookup("flagname").NoOptDefVal = "4321"
不同參數(shù)結(jié)果如下:
命令行參數(shù) | 結(jié)果值 |
---|---|
--flagname=1357 | ip=1357 |
--flagname | ip=4321 |
[nothing] | ip=1234 |
棄用/隱藏標(biāo)志
使用 flags.MarkDeprecated
可以棄用一個標(biāo)志,使用 flags.MarkShorthandDeprecated
可以棄用一個簡短標(biāo)志,使用 flags.MarkHidden
可以隱藏一個標(biāo)志。
package main import ( "fmt" "os" "github.com/spf13/pflag" ) func main() { flags := pflag.NewFlagSet("test", pflag.ExitOnError) var ip = flags.IntP("ip", "i", 1234, "help message for ip") var boolVar bool flags.BoolVarP(&boolVar, "boolVar", "b", true, "help message for boolVar") var h string flags.StringVarP(&h, "host", "H", "127.0.0.1", "help message for host") // 棄用標(biāo)志 flags.MarkDeprecated("ip", "deprecated") flags.MarkShorthandDeprecated("boolVar", "please use --boolVar only") // 隱藏標(biāo)志 flags.MarkHidden("host") flags.Parse(os.Args[1:]) fmt.Printf("ip: %d\n", *ip) fmt.Printf("boolVar: %t\n", boolVar) fmt.Printf("host: %+v\n", h) }
查看使用幫助:
$ go run main.go -h Usage of test: --boolVar help message for boolVar (default true) pflag: help requested
從打印結(jié)果可以發(fā)現(xiàn),棄用標(biāo)志 ip
時,其對應(yīng)的簡短標(biāo)志 i
也會跟著被棄用;棄用 boolVar
所對應(yīng)的簡短標(biāo)志 b
時,boolVar
標(biāo)志會被保留;host
標(biāo)志則完全被隱藏。
指定如下命令行參數(shù)運(yùn)行示例:
$ go run main.go --ip 1 --boolVar=false -H localhost Flag --ip has been deprecated, deprecated ip: 1 boolVar: false host: localhost
打印信息中會提示用戶 ip
標(biāo)志已經(jīng)棄用,不過使用 --ip 1
指定的參數(shù)值依然能夠生效。
隱藏的 host
標(biāo)志使用 -H localhost
指定參數(shù)值同樣能夠生效。
指定如下命令行參數(shù)運(yùn)行示例:
$ go run main.go -i 1 -b=false --host localhost Flag --ip has been deprecated, deprecated Flag shorthand -b has been deprecated, please use --boolVar only ip: 1 boolVar: false host: localhost
打印信息中增加了一條簡短標(biāo)志 -b
已被棄用的提示,指定參數(shù)值依然生效。
對于棄用的 ip
標(biāo)志,使用簡短標(biāo)志形式傳慘 -i 1
同樣生效。
支持 flag 類型
由于 pflag 對 flag 包兼容,所以可以在一個程序中混用二者:
package main import ( "flag" "fmt" "github.com/spf13/pflag" ) func main() { var ip *int = pflag.Int("ip", 1234, "help message for ip") var port *int = flag.Int("port", 80, "help message for port") pflag.CommandLine.AddGoFlagSet(flag.CommandLine) pflag.Parse() fmt.Printf("ip: %d\n", *ip) fmt.Printf("port: %d\n", *port) }
其中,ip
標(biāo)志是使用 pflag.Int()
聲明的,port
標(biāo)志則是使用 flag.Int()
聲明的。只需要通過 AddGoFlagSet
方法將 flag.CommandLine
注冊到 pflag 中,那么 pflag 就可以使用 flag 中聲明的標(biāo)志集合了。
運(yùn)行示例結(jié)果如下:
$ go run main.go --ip 10 --port 8000
ip: 10
port: 8000
總結(jié)
本文主要介紹了 Go第三方標(biāo)志包 pflag 的特點(diǎn)及用法。
首先介紹了 pflag 的基本使用方法,包括聲明標(biāo)志、解析命令行參數(shù)、獲取標(biāo)志值等。接著介紹了 pflag 的進(jìn)階用法,例如自定義 FlagSet
、使用 pflag.<Type>P
方法來支持簡短標(biāo)志。之后又對命令行標(biāo)志語法進(jìn)行了講解,對于布爾值、非布爾值和簡短標(biāo)志,都有各自不同的語法。我們還講解了如何借助 pflag.NormalizedName
給標(biāo)志起一個或多個別名、規(guī)范化標(biāo)志名。然后介紹了 NoOptDefVal
的作用和如何棄用/隱藏標(biāo)志。最后通過示例演示了如何在一個程序中混用 flag 和 pflag。
彩蛋:不知道你有沒有發(fā)現(xiàn),示例中的 ip
標(biāo)志的名稱其實代表的是 int pointer
而非 Internet Protocol Address
。ip
標(biāo)志源自官方示例,不過我順勢而為又聲明了 port
、host
標(biāo)志,算是一個程序中的諧音梗 :)。
以上就是Go語言中命令行參數(shù)解析工具pflag的使用指南的詳細(xì)內(nèi)容,更多關(guān)于Go pflag命令行參數(shù)解析的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Go語言循環(huán)遍歷含有中文的字符串的方法小結(jié)
這篇文章主要介紹了Go語言循環(huán)遍歷含有中文的字符串的幾種方法,文章通過代碼示例講解的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴跟著小編一起來看看吧2023-07-07Go 實現(xiàn)一次性打包各個平臺的可執(zhí)行程序
這篇文章主要介紹了Go 實現(xiàn)一次性打包各個平臺的可執(zhí)行程序,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-12-12利用golang實現(xiàn)封裝trycatch異常處理實例代碼
Go語言追求簡潔優(yōu)雅,所以go語言不支持傳統(tǒng)的 try…catch…finally 這種異常,最近發(fā)現(xiàn)了不錯的trycatch包,下面這篇文章主要跟大家分享了關(guān)于利用golang實現(xiàn)封裝trycatch異常處理的實例代碼,需要的朋友可以參考下。2017-07-07Go語言CSP并發(fā)模型goroutine及channel底層實現(xiàn)原理
這篇文章主要為大家介紹了Go語言CSP并發(fā)模型goroutine?channel底層實現(xiàn)原理,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05