Part2

GO語言的flag包:

flag的定義:
flag.String(), Bool(), Int() //這裏這是列舉了幾個
然後就是兩種定義的方式:

var ip = flag.Int("flagname", 1234, "help message for flagname") //ip 爲指針類型,Int或者
String返回的都是指針類型

flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname")//此處需要在外面手動定義flagvar

第一種方式是返回一個值,第二種方式是把變量的傳進去。

通過flag.Var()綁定自定義類型,自定義類型需要實現Value接口(Receives必須爲指針),如:

flag.Var(&flagVal, "name", "help message for flagname")

對於這種類型的flag,默認值爲該變量類型的初始值

解析命令行參數

Go語言標準庫中的flag包專門用於接收和解析命令參數。

flag的解析
flag.Parse()
解析函數將會在碰到第一個非flag命令行參數時停止,非flag命令行參數是指不滿足命令行語法的參數,如命令行參數爲cmd --flag=true abc則第一個非flag命令行參數爲“abc”

flag解析後的參數使用
fmt.Println("ip has value ", *ip)
fmt.Println("flagvar has value ", flagvar)
命令行語法
-flag //只支持bool類型
-flag=x
-flag x //只支持非bool類型
注意第三種只支持非bool類型,原因就是如果文件的名字爲false的話那麼就會產生歧義。看例子:

package main

import (
    "flag"
    "fmt"
)

var name string
var right bool
func init() {
    flag.StringVar(&name, "name", "everyone", "The greeting object.")
    flag.BoolVar(&right, "check", false, "The right ob")
}

func main() {
    flag.Parse()
    fmt.Println(name)
    fmt.Println(right)
}

這裏採用的還是老師的例子,只多加了個bool類型。
我們編譯之後生成exe

demo4.exe -check false
demo4.exe -check

對於這兩種都會採用默認的bool類型true。

demo4.exe -check=false

只有這樣的時候,纔會採用false

-name 無論有無=都會顯示傳入的字符串。

eg2:

package main

import (
	"flag"
	"fmt"
)

var name string
var name1 string

func init() {
	flag.StringVar(&name, "name", "Nobody", "請設置名字")
	name1 = *flag.String("name1", "Nobody", "請設置名字")
}

func main() {
	flag.Parse()
	fmt.Printf("Hello, %s!\n", name)
	fmt.Printf("Hello, %s!\n", name1)
}

函數flag.StringVar接受4個參數:

  • 第1個參數,是用於存儲該命令參數的值的地址,就是示例中聲明的變量name的地址,由表達式&name表示
  • 第2個參數,是爲了指定該命令參數的名稱,這里是name
  • 第3個參數,是爲了指定在未追加該命令參數時的默認值,這里是Nobody
  • 第4個參數,是該命令參數的簡短說明,這在打印命令說明時會用到

還有一個函數flag.String和flag.StringVar類似。就是沒有第一個參數了,而是直接返回一個已經分配好的用於存儲命令參數值的地址。

函數調用順序

函數flag.Parse用於真正解析命令參數,並把它們的值賦給相應的變量。
該函數的調用必須在所有命令參數的聲明和設置之後,並且在讀取任何命令參數值之前。
也就是像例子裏做的,把參數設置的語句放在init函數裏先執行,然後把flag.Parse放在main函數的開頭,在調用使用命令行參數的語句之前。

自定製參數使用說明

在上面的例子中,執行的時候帶上-h或者–help參數,就能看到如下的使用說明:

PS G:\Steed\Documents\Go\src\Go36\article02\example01> go run main.go --help
Usage of C:\Users\Steed\AppData\Local\Temp\go-build897600374\command-line-arguments\_obj\exe\main.exe:
  -name string
        請設置名字 (default "Nobody")
exit status 2
PS G:\Steed\Documents\Go\src\Go36\article02\example01>

輸出的第一行在Usage of後面一長串的路徑,是go run命令構建上述命令源碼文件時臨時生成的可執行文件的完整路徑。如果是編譯之後再執行,就是可執行文件的相對路徑,就沒那麼難看了。
並且這一行的說明內容是可以自定製的,接下來就是通過包提供的方法對這行說明進行自定製。這3種方法是一層一層更加接近底層的調用的。

通過flag.Usage定製

最簡單的一種定製方式就是對變量flag.Usage重新賦值。爲該變量定義一個函數,在要打印說明的時候,其實就是調用執行了這個方法:

package main

import (
	"flag"
	"fmt"
	"os"
)

var name string

func init() {
    // 下面2句語句的順序隨意
	flag.StringVar(&name, "name", "Nobody", "請設置名字")
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage of %s\n", "Say Hello")
		flag.PrintDefaults()
	}
}

func main() {
	flag.Parse()
	fmt.Printf("Hello, %s!\n", name)
}

flag.Usage的賦值必須在調用flag.Parse()之前。

通過flag.CommandLine定製

在調用flag包中的一些函數(比如StringVar、Parse等等)的時候,實際上是在調用flag.CommandLine變量的對應方法。
flag.CommandLine相當於默認情況下的命令參數容器。通過對flag.CommandLine重新賦值,就可以更深層次地定製當前命令源碼文件的參數使用說明。
僅修改之前的init函數部分,去掉flag.Usage的賦值語句,改爲通過對flag.CommandLine賦值來進行定製:

func init() {
    // flag.CommandLine對象的創建必須在前面執行
	flag.CommandLine = flag.NewFlagSet("", flag.ExitOnError)
	//flag.CommandLine = flag.NewFlagSet("", flag.PanicOnError)
	flag.CommandLine.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage of %s\n", "Say Hello")
		flag.PrintDefaults()
	}
	flag.StringVar(&name, "name", "Nobody", "請設置名字")
}

這樣修改後的效果和之前完全一致。這裏主要出分離出了下面這句:

flag.CommandLine = flag.NewFlagSet("", flag.ExitOnError)

這句的第一個參數應該是沒有用的了,這裏傳入空字符串。
第二個參數可以是一下的3個常量:

const (
	ContinueOnError ErrorHandling = iota // Return a descriptive error.
	ExitOnError                          // Call os.Exit(2).
	PanicOnError                         // Call panic with a descriptive error.
)

定義在解析遇到問題後,是執行何種操作。默認的就是ExitOnError,所以在–help執行打印說明後,最後一行會出現“exit status 2”,以狀態碼2退出。
這裏可以根據需要定製爲拋出Panic。

創建私有命令參數容器

這裏的代碼上上面的例子差不多,依然是調用flag.NewFlagSet()創建命令參數容器。不過這次把容器賦值給自定義的變量:

package main

import (
	"flag"
	"fmt"
	"os"
)

var name string
var cmdLine = flag.NewFlagSet("cmdLine Say Hello", flag.ExitOnError)

func init() {
	cmdLine.StringVar(&name, "name", "Nobody", "請設置名字")
}

func main() {
	cmdLine.Parse(os.Args[1:])
	fmt.Printf("Hello, %s!\n", name)
}

首先通過命令 var cmdLine = flag.NewFlagSet("cmdLine Say Hello", flag.ExitOnError)創建了私有的命令參數容器。
然後,之後其他所有方法的調用都通過這個變量來調用的。
上面這樣做之後,就完全脫離了flag.CommandLine。而是使用 *flag.FlagSet 類型的變量 cmdLine 進行各種調用了。該類型擁有很多方法,可以繼續探索。
主要是可以更靈活地定製命令參數容器。並且你的定製完全不會影響到全局變量flag.CommandLine。

思考題

1. 默認情況下,我們可以讓命令源碼文件接受哪些類型的參數值?
這個從flag包的代碼包裏面就可以看到,這裏貼一下班裏一個同學的答案

    int(int|int64|uint|uint64),
    float(float|float64)
    string,
    bool,
    duration(時間),
    var(自定義)


2. 我們可以把自定義的數據類型作爲參數值的類型嗎?如果可以,怎樣做?

    當然是可以的:
    // Var defines a flag with the specified name and usage string. The type and
    // value of the flag are represented by the first argument, of type Value, which
    // typically holds a user-defined implementation of Value. For instance, the
    // caller could create a flag that turns a comma-separated string into a slice
    // of strings by giving the slice the methods of Value; in particular, Set would
    // decompose the comma-separated string into the slice.
    func Var(value Value, name string, usage string) {
        CommandLine.Var(value, name, usage)
    }

    type pepoInfo struct{
        name string
        age int
        homeAdress string
    }

    func (p * pepoInfo) Set(val string) error{
        peopleList := strings.Split(val, ",")
        p.name = peopleList[0]
        p.age,_ =  strconv.Atoi(peopleList[1])
        p.homeAdress = peopleList[2]
        return nil
    }   

    func (s *pepoInfo) String() string {
        infoList := strings.Split("wenxuwan,20,山東", ",")
        s.name = infoList[0]
        s.age,_ = strconv.Atoi(infoList[1])
        s.homeAdress = infoList[2]
        return "It's none of my business"
    }
    func main(){

    var people pepoInfo
    flag.Var(&people, "info", "hao ma fan")
    flag.Parse()
    //打印結果slice接收到的值
    fmt.Println(people)
    }

demo4.exe --info wenxuwan,10,shouguang
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章