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