日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

關于Golang標準庫flag的全面講解_Golang

作者:程序員讀書??????? ? 更新時間: 2022-10-31 編程語言

前言:

今天來聊聊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)

上面的程序中,我們定義了pport兩個參數,并將其綁定到變量port,因此通過下面兩條命令都可以獲取參數:

$ go run main.go -p 111
$ go run main.go -port 111

小結

在這篇文章中,我們全面講解了flag庫,看完這篇文章,相信你可以學習到以下幾點:

  • 在Go程序員中如何使用flag庫解析命令行參數。
  • flag庫解析命令行參數的原理。
  • 怎么讓flag庫支持我們自定義的數據類型。
  • 怎么定義命令行參數短選項。

原文鏈接:https://juejin.cn/post/7098245145744965663

欄目分類
最近更新