Go標準庫flag包的“小陷阱”
,這意味著Go標準庫可開箱即用,為Gopher提供了功能豐富的常用工具包,足以應付多數日常開發所需。尤其在Go語言擅長的領域,Go標準庫工具包更是有著廣泛的應用。下圖是[Go官方2020年使用者調查]的結果:
我們看到cli([command-line interface])領域開發佔據了Go語言應用的Top2位置,僅次於開發API/RPC服務。而普通cli應用的開發總是離不開標準庫的flag包。
flag包估計是很多gopher入門go語言的必經之路。flag使用起來十分簡單,功能也不差,常規的命令列程式的flag形式它都支援,比如下面這個示例程式:
// flag_demo1.go
package main
import (
"flag"
"fmt"
)
var (
n = flag.Int("n", 1234, "help message for flag n")
)
func main() {
flag.Parse()
fmt.Printf("n=%dn", *n)
}
flag_demo1僅支援一個cmd flag: -n。我們可以像下面這樣使用flag_demo1這個cli程式,為變數n傳值:
$go build flag_demo1.go
$./flag_demo1
n=1234 //預設值
$./flag_demo1 -n 1111
n=1111
$./flag_demo1 --n 1111
n=1111 // --n和-n是等價的
$./flag_demo1 -n=2222
n=2222
$./flag_demo1 --n=2222
n=2222
我們看到,我們可以使用下面四種形式為一個整型flag變數傳引數:
- -n value
- –n value
- -n=value
- –n=value
無論使用哪種形式,它們起到的效果是等價的。
但是當我們將flag放置在cli應用的最後面時,我們要小心了:
$./flag_demo1 show -n=2222
n=1234
我們看到雖然我們在命令列因公flag_demo1的引數列表中進行了-n=2222的引數傳遞,但flag_demo1的flag包直接無視了這次引數傳遞,而將變數n置為預設值1234了。
這是因為flag包的命令列引數的解析邏輯是:當碰到第一個非flag引數時,便停止解析。上面命令列執行時傳入的“show”並非flag_demo1的flag引數,因此flag包就會在解析完show後停止後面命令列引數(-n=2222)的解析,於是上述命令列就等價於:
$./flag_demo1 show
n=1234
那麼n=1234就不足為奇了!這被我稱為flag包的第一個“小陷阱”。不僅像“show”這樣的非flag引數可以阻斷flag包對命令列引數列表的繼續解析,單獨存在的“-”和“–”也具有同樣的“阻斷功能”:
$./flag_demo1 -- -n=2222
n=1234
$./flag_demo1 - -n=2222
n=1234
我們也常在命令列flag引數中使用bool類的引數值,比如下面示例:
// flag_demo2.go
package main
import (
"flag"
"fmt"
)
var (
n = flag.Int("n", 1234, "int value for flag n")
b1 = flag.Bool("b1", false, "bool value for flag b1")
b2 = flag.Bool("b2", false, "bool value for flag b2")
)
func main() {
flag.Parse()
fmt.Printf("n=%dn", *n)
fmt.Printf("b1=%tn", *b1)
fmt.Printf("b2=%tn", *b2)
}
這個示例中有兩個bool型flag引數和一個int型flag引數,我們來執行一下該cli應用:
$go build flag_demo2.go
$./flag_demo2 -b1 true -b2 true -n 2222
n=1234
b1=true
b2=false
執行的輸出似乎與預期結果不符啊!為什麼b2變數的值依舊為false,變數n的值為啥不是2222?難道在多個flag引數下,flag包有bug?其實不是的!
問題就在於bool型別flag引數的特殊性。由於一些原因,bool型別flag引數不支援“-arg value”形式,只支援下面兩種形式:
-arg
-arg=value
我們按bool型別flag引數的正確傳遞方法再執行一下上面的flag_demo2:
$./flag_demo2 -b1=true -b2=true -n 2222
n=2222
b1=true
b2=true
這回的輸出與預期吻合。
但細心的朋友可能會發現,之前的錯誤用法:
$./flag_demo2 -b1 true -b2 true -n 2222
十分有迷惑性!因為變數b1的輸出值是符合預期的,這讓人誤以為flag引數的傳遞方法是正確無誤的。這種“錯覺”讓gopher不知不覺地掉入了**flag包的第二個“陷阱”**中。而上面的錯誤flag引數值傳遞實質上等價於:
$./flag_demo2 -b1
這就是為什麼b1=true,而b2和n均為預設值的原因了!
flag包是我們日常最廣泛使用的標準庫包之一,因此務必瞭解flag包可能被誤用的情況,別掉入flag包的“小陷阱”中!陷阱雖小,出事是大,希望這裡的分享能幫助大家在日常繞過這些“陷阱”!
Go技術專欄“”正在慕課網火熱熱銷中!本專欄主要滿足廣大gopher關於Go語言進階的需求,圍繞如何寫出地道且高質量Go程式碼給出50條有效實踐建議,上線後收到一致好評!歡迎大家訂
閱!
我的網課“”在慕課網熱賣中,歡迎小夥伴們訂閱學習!
講師主頁:
講師部落格:
專欄:
實戰課:
免費課:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/3034/viewspace-2797836/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Go標準包-http包serverGoHTTPServer
- go標準庫-log包原始碼學習Go原始碼
- Go標準包—http clientGoHTTPclient
- Go標準包——net/rpc包的使用GoRPC
- Go標準庫ContextGoContext
- Go 的 golang.org/x/ 系列包和標準庫包有什麼區別?Golang
- 標準庫 fmt 包的基本使用
- Go標準庫:Go template用法詳解Go
- go語言標準庫 - logGo
- go語言標準庫 - timeGo
- golang標準庫的分析os包(6)Golang
- Golang中的unsafe標準庫包Golang
- go語言標準庫 - strconvGo
- 標準庫 http 包的簡單實用HTTP
- go語言標準庫 - regexpGo
- Go標準庫所有方法使用例子Go
- Go 標準庫 —— sync.Mutex 互斥鎖GoMutex
- Go net/http 標準庫思維導圖GoHTTP
- Go Web學習 -標準庫 net/http 使用GoWebHTTP
- Go語言學習(1)——標準庫fmtGo
- go 的 json 標準庫有接班人了GoJSON
- 「Golang成長之路」標準庫之os包Golang
- 「Golang成長之路」標準庫之time包Golang
- Go 標準庫之 GoRequests 介紹與基本使用Go
- Go 創始人 Rob Pike 反對在 Go 1.18 標準庫中引入泛型支援:建議不要改動 Go 1.18 中的標準庫Go泛型
- Python標準庫04 檔案管理 (部分os包,shutil包)Python
- Python標準庫分享之儲存物件 (pickle包,cPickle包)Python物件
- Go Standard library - flagGo
- Python標準庫分享之檔案管理 (部分os包,shutil包)Python
- Python 快速教程(標準庫05):儲存物件 (pickle包,cPickle包)Python物件
- Go 常用標準庫之 fmt 介紹與基本使用Go
- Go Web學習(1)——標準庫http實現serverGoWebHTTPServer
- [HotSpot VM] JVM調優的"標準引數"的各種陷阱HotSpotJVM
- Python標準庫10 多程式初步 (multiprocessing包)Python
- Python標準庫11 多程式探索 (multiprocessing包)Python
- Python 快速教程(標準庫06):子程式 (subprocess包)Python
- CUJ:標準庫:標準庫中的搜尋演算法 (轉)演算法
- Python標準庫12 數學與隨機數 (math包,random包)Python隨機random