網站首頁 編程語言 正文
前言:
今天來聊聊Go語言標準庫中一個非常簡單的庫flag,這個庫的代碼量只有1000行左右,卻提供了非常完善的命令行參數解析功能。
命令行參數
如果你有使用過類Unix(比如MacOS,Linux)等操作系統,相信你應該明白命令參數是什么,比如下面的兩條命令:
$ mysql -u root -p 123456 $ ls -al
第一條命令是MySQL的客戶端,其-u root
和-p 123456
就是命令行參數,第二條命令用于顯示當前目錄的文件及目錄,該命令中-al
就是命令行參數。
flag庫的作用就是幫我們將命令后面的選項參數解析到對應的變量中。
使用詳解
要了解一個庫,須從使用開始,下面我們通過一個簡單的示例來快速了解flag庫的使用,這個示例可以接收從命令行傳遞的用于連接數據庫的參數,
代碼如下:
package main import ( "flag" "fmt" ) var ( host string dbName string port int user string password string ) func main() { flag.StringVar(&host, "host", "", "數據庫地址") flag.StringVar(&dbName, "db_name", "", "數據庫名稱") flag.StringVar(&user, "user", "", "數據庫用戶") flag.StringVar(&password, "password", "", "數據庫密碼") flag.IntVar(&port, "port", 3306, "數據庫端口") flag.Parse() fmt.Printf("數據庫地址:%s\n", host) fmt.Printf("數據庫名稱:%s\n", dbName) fmt.Printf("數據庫用戶:%s\n", user) fmt.Printf("數據庫密碼:%s\n", password) fmt.Printf("數據庫端口:%d\n", port) }
在命令行窗口輸入以下命令,開始運行程序
go run main.go -host=localhost -user=test -password=123456 -db_name=test -port=3306
運行結束,輸出結果如下所示:
數據庫地址:localhost
數據庫名稱:test
數據庫用戶:test
數據庫密碼:123456
數據庫端口:3306
上面的示例就是一個解析命令行選項參數的模板,包括下面三個步驟:
- 定義好接收參數的變量。
- 調用flag.StringVar()等函數將命令行選項與變量綁定。
- 調用flag.Parse()函數,開始解析變量。
在上面程序中,我們用了StringVar函數綁定字符串類型的參數,用了IntVar函數綁定整數類型的參數,除了字符串和整型,flag支持boolean,Duration,float64,Int64,uint,uint64等類型,下面是這些函數的定義,用法與StringVar相同。
func BoolVar(p *bool, name string, value bool, usage string) func DurationVar(p *time.Duration, name string, value time.Duration, usage string) func Float64Var(p *float64, name string, value float64, usage string) func Int64Var(p *int64, name string, value int64, usage string) func IntVar(p *int, name string, value int, usage string) func StringVar(p *string, name string, value string, usage string) func Uint64Var(p *uint64, name string, value uint64, usage string) func UintVar(p *uint, name string, value uint, usage string)
上面列出的函數帶有Var
后綴,表示需要我們自己傳遞一個變量去接收命令行參數,而flag在這些函數有基礎上,封裝了下面列表的函數,這些函數沒有Var
后綴,跟上面的函數相比少了一個參數,卻多了一個返回值,這個返回值就是接收命令參數的變量指針。
func Bool(name string, value bool, usage string) *bool func Duration(name string, value time.Duration, usage string) *time.Duration func Float64(name string, value float64, usage string) *float64 func Int(name string, value int, usage string) *int func Int64(name string, value int64, usage string) *int64 func String(name string, value string, usage string) *string func Uint(name string, value uint, usage string) *uint func Uint64(name string, value uint64, usage string) *uint64
所以我們把上面的示例改寫為以下的樣子:
package main import ( "flag" "fmt" ) func main() { host := flag.String("host", "", "數據庫地址") dbName := flag.String("db_name", "", "數據庫名稱") user := flag.String("user", "", "數據庫用戶") password := flag.String("password", "", "數據庫密碼") port := flag.Int("port", 3306, "數據庫端口") flag.Parse() fmt.Printf("數據庫地址:%s\n", *host) fmt.Printf("數據庫名稱:%s\n", *dbName) fmt.Printf("數據庫用戶:%s\n", *user) fmt.Printf("數據庫密碼:%s\n", *password) fmt.Printf("數據庫端口:%d\n", *port) }
另外,運行程序時,在后面跟上-h
或--help
來查看命令的參數選項,如:
go run main.go --help Usage of main: -db_name string 數據庫名稱 -host string 數據庫地址 -password string 數據庫密碼 -port int 數據庫端口 (default 3306) -user string 數據庫用戶
選項語法
flag支持以下三種命令行格式,參數前面的-
也可以換成--
,在flag庫中,--
并不是表示長選項的意思。
cmd -flag cmd -flag=x cmd -flag x
- 第一種只用布爾值的選項,如果該參數出現,則為true,不出則為默認值,而其他數據類型不能使用這種格式傳值。
- 第二種可適用任何類型,因此也是最常用的格式。
- 第三種不可用于布爾值的選項。
flag在解析參數時,如果遇到第一個非選項參數(不是以-或--開頭的)或終止符--,就會停止解析,
比如上面的示例中,我們將運行命令改成下面的樣子:
go run main.go -host=localhost noflag -user=test -password=123456 -db_name=test -port=3306
運行結果如下,可以看到解析-host參數之后遇到了noflag這樣的非選項參數,flag就停止解析了,所以后面的參數都只輸出了默認值。
數據庫地址:localhost
數據庫名稱:
數據庫用戶:
數據庫密碼:
數據庫端口:3306
整數類型的參數可以接收十進制、八進制,十六進制的參數,布爾型可以接收下面列出參數
1, 0, t, f, T, F, true, false, TRUE, FALSE, True, False
Duration
類型的參數接收可以被time.ParseDuration()
解析的參數。
flag是怎么解析參數的?
我們知道flag庫是用于命令行解析的,但其內部是怎么解析的呢?下面我們來分析一下
一個命令行參數包含以下四個部分:
- 接收參數的變量
- 參數名稱
- 默認值
- 參數說明
所以flag設置命令行參數的函數有四個參數,比如:
var p int flag.IntVar(&p,"port",3306,"數據庫端口")
flag內部有一個名稱CommandLine的變量,其類型為FlagSet,如:
var CommandLine = NewFlagSet(os.Args[0], ExitOnError)
FlagSet就是一個命令行參數的集合體,當我們調用諸如IntVar
這類的函數時,就是將命令行的默認值
、參數說明
,參數名稱
,接收參數的變量
等信息告訴flag庫,而flag內部會讓CommandLine來處理,用這些信息創建Flag類型的變量,將添加到這個集合體中。
flag := &Flag{name, usage, value, value.String()}
最后,當我們調用flag.Parse函數時,實際就是調用FlagSet結構體的Parse函數將命令參數解析到變量中,flag.Parse函數代碼如下:
func Parse() { CommandLine.Parse(os.Args[1:]) }
從上面的代碼我們也可以看出來,FlagSet的Parse函數最終是通過獲取os.Args數組的數據來解析命令行參數的。
即然我們知道flag是通過類型為FlagSet的變量CommandLine來處理命令行參數的,那其實我們也可以自己創建一個FlagSet類型的變量來處理命令行參數,
所以我們可以將上面的例改成下面的樣子:
package main import ( "flag" "fmt" "os" ) func main() { //自己創建一個命令行參數的集合 var flagSet = flag.NewFlagSet("my flag", flag.ExitOnError) host := flagSet.String("host", "", "數據庫地址") dbName := flagSet.String("db_name", "", "數據庫名稱") user := flagSet.String("user", "", "數據庫用戶") password := flagSet.String("password", "", "數據庫密碼") port := flagSet.Int("port", 3306, "數據庫端口") //解析命令行參數,從os.Args的第二個元素開始,第一個元素是命令本身 flagSet.Parse(os.Args[1:]) fmt.Printf("數據庫地址:%s\n", *host) fmt.Printf("數據庫名稱:%s\n", *dbName) fmt.Printf("數據庫用戶:%s\n", *user) fmt.Printf("數據庫密碼:%s\n", *password) fmt.Printf("數據庫端口:%d\n", *port) }
另外,我們已經知道了flag解析參數的來源是os.Args這樣的字符串數組,那我們也可以模擬一個這樣的數組,將數組解析到變量之中,而不需要去解析os.Args數組,
下面的例子就是這樣做的:
package main import ( "flag" "fmt" ) func main() { //模擬os.Args數組,定義一個參數數組 var params = []string{"-host", "127.0.0.1", "-db_name", "test", "-user", "test", "-password", "abcdef", "-port", "13306"} var flagSet = flag.NewFlagSet("my flag", flag.ExitOnError) host := flagSet.String("host", "", "數據庫地址") dbName := flagSet.String("db_name", "", "數據庫名稱") user := flagSet.String("user", "", "數據庫用戶") password := flagSet.String("password", "", "數據庫密碼") port := flagSet.Int("port", 3306, "數據庫端口") //解析自定義的參數數組 flagSet.Parse(params) fmt.Printf("數據庫地址:%s\n", *host) fmt.Printf("數據庫名稱:%s\n", *dbName) fmt.Printf("數據庫用戶:%s\n", *user) fmt.Printf("數據庫密碼:%s\n", *password) fmt.Printf("數據庫端口:%d\n", *port) }
運行程序,在命令后面不需要跟命令行參數,如下:
go run main.go
運行后結果如下:
數據庫地址:127.0.0.1
數據庫名稱:test
數據庫用戶:test
數據庫密碼:abcdef
數據庫端口:13306
自定義數據類型
如果flag提供的數據類型不能滿足我們的需要,我們也可以自定義類型,自定義類型需要實現flag中的Value接口,該接口定義如下:
type Value interface { String() string Set(string) error }
Value
類型的String()用于打印數值,而Set方法則用于flag包將命令行參數設置到Value
類型中。
下面是一個自定義類型的示例程序:
package main import ( "flag" "fmt" "strings" ) type Users []string func (u *Users) Set(val string) error { *u = strings.Split(val, ",") return nil } func (u *Users) String() string { str := "[" for _, v := range *u { str += v } return str + "]" } func main() { var u Users flag.Var(&u, "u", "用戶列表") flag.Parse() for _, v := range u { fmt.Println(v) } }
運行結果:
?go run main.go -u=小明,小張,小紅,小剛
小明
小張
小紅
小剛
從上面的示例中我們可以總結自定義類型的幾個步驟:
- 定義一個實現flag.Value接口的類型,并實現String和Set方法。
- 使用flag.Var函數將類型綁定到類型參數。
- 調用flag.Parse()解析命令行參數。
短選項
我們在使用Linux命令的時候,發現很多命令的參數是有分短選項和長選項的,不過flag庫并不支持短選項;當然也有變通的方式,比如我們可以自己定義一個長選項和短選項,
如:
var port int flag.IntVar(&port, "p", 3306, "數據庫端口") flag.IntVar(&port, "port", 3306, "數據庫端口") flag.Parse() fmt.Println(port)
上面的程序中,我們定義了p
和port
兩個參數,并將其綁定到變量port
,因此通過下面兩條命令都可以獲取參數:
$ go run main.go -p 111 $ go run main.go -port 111
小結
在這篇文章中,我們全面講解了flag庫,看完這篇文章,相信你可以學習到以下幾點:
- 在Go程序員中如何使用flag庫解析命令行參數。
- flag庫解析命令行參數的原理。
- 怎么讓flag庫支持我們自定義的數據類型。
- 怎么定義命令行參數短選項。
原文鏈接:https://juejin.cn/post/7098245145744965663
相關推薦
- 2022-10-02 利用Android封裝一個有趣的Loading組件_Android
- 2022-04-11 Tomcat訪問不到web應用報錯ORA-01882: 未找到時區的解決方案
- 2023-01-12 keepalived?+?nginx?實現高可用方案_nginx
- 2022-06-04 Qt實現自定義驗證碼輸入框控件的方法_C 語言
- 2022-04-23 uniapp文件上傳(任意文件,多文件上傳)
- 2022-09-09 React前端DOM常見Hook封裝示例下_React
- 2022-09-01 MongoDB實現查詢、分頁和排序操作以及游標的使用_MongoDB
- 2023-03-01 PostgreSQL生成列實現過程介紹_PostgreSQL
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支