go flag

flag 用法參考:

https://golang.google.cn/pkg/flag/


1. 命令源碼文件怎樣接收參數

package main

import (
    "flag" #導入flag包
    "fmt"
)

var name string

func init(){
    flag.StringVar(&name,"name","everyone","The greting object") # 
}

func main(){
    flag.Parse() #函數flag.Parse用於真正解析命令參數,並把它們的值賦給相應的變量。
    fmt.Printf("hello,%s!\n",name)
}

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

第 1 個參數是用於存儲該命令參數值的地址,具體到這裏就是在前面聲明的變量name的地址了,由表達式&name表示。

第 2 個參數是爲了指定該命令參數的名稱,這裏是name。

第 3 個參數是爲了指定在未追加該命令參數時的默認值,這裏是everyone。

第 4 個函數參數,即是該命令參數的簡短說明了,這在打印命令說明時會用到。

go run demo2.go -name="huaihe"
Hello, huaihe!


2. 怎樣在運行命令源碼文件的時候傳入參數,又怎樣查看參數的使用說明


如果想查看該命令源碼文件的參數說明,可以這樣做:

go run demo2.go --help
Usage of /var/folders/4w/lv_wjx0d3b3ff30w0bn_65xc0000gn/T/go-build966482306/b001/exe/demo2: #構建上述命令源碼文件時臨時生成的可執行文件的完整路徑
  -name string
        The greeting object. (default "everyone")
exit status 2


如果我們先構建這個命令源碼文件再運行生成的可執行文件,像這樣:

go build demo2.go
ls demo2
demo2
./demo2 --help
Usage of ./demo2:
  -name string
        The greeting object. (default "everyone")
./demo2 -name="huaihe"
Hello, huaihe!


3. 怎樣自定義命令源碼文件的參數使用說明

這有很多種方式,最簡單的一種方式就是對變量flag.Usage重新賦值。flag.Usage的類型是func(),即一種無參數聲明且無結果聲明的函數類型。flag.Usage變量在聲明時就已經被賦值了,所以我們才能夠在運行命令go run demo2.go --help時看到正確的結果。注意,對flag.Usage的賦值必須在調用flag.Parse函數之前。

package main

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

var name string

func init() {
    flag.StringVar(&name, "name", "everyone", "The greting object")
}

func main() {
    flag.Usage = func() {
        fmt.Fprintf(os.Stderr, "Usage of %s:\n", "question")
        flag.PrintDefaults()
    }
    flag.Parse()
    fmt.Printf("hello,%s!\n", name)
}
go run demo2.go --help
#這裏並沒有打印臨時文件位置,而是打印了os.Stderr,因爲此時os.Stderr爲空
Usage of question:
  -name string
        The greting object (default "everyone")
exit status 2

再深入一層,我們在調用flag包中的一些函數(比如StringVar、Parse等等)的時候,實際上是在調用flag.CommandLine變量的對應方法。

flag.CommandLine相當於默認情況下的命令參數容器。所以,通過對flag.CommandLine重新賦值,我們可以更深層次地定製當前命令源碼文件的參數使用說明。

現在我們把main函數體中的那條對flag.Usage變量的賦值語句註銷掉,然後在init函數體的開始處添加如下代碼:

package main

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

var name string

func init() {
    flag.CommandLine = flag.NewFlagSet("", flag.ExitOnError)
    flag.CommandLine.Usage = func() {
        fmt.Fprintf(os.Stderr, "Usage of %s:\n", "question")
        flag.PrintDefaults()
    }
    flag.StringVar(&name, "name", "everyone", "The greting object")
}

func main() {
    flag.Parse()
    fmt.Printf("hello,%s!\n", name)
}
go run demo2.go --help
Usage of question:
  -name string
        The greting object (default "everyone")
exit status 2

其輸出會與上一次的輸出的一致。不過後面這種定製的方法更加靈活。比如,當我們把爲flag.CommandLine賦值的那條語句改爲:

package main

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

var name string

func init() {
    flag.CommandLine = flag.NewFlagSet("", flag.PanicOnError) //注意這裏修改爲flag.PanicOnError
    flag.CommandLine.Usage = func() {
        fmt.Fprintf(os.Stderr, "Usage of %s:\n", "question")
        flag.PrintDefaults()
    }
    flag.StringVar(&name, "name", "everyone", "The greting object")
}

func main() {
    flag.Parse()
    fmt.Printf("hello,%s!\n", name)
}
go run demo2.go --help
Usage of question:
  -name string
        The greting object (default "everyone")
panic: flag: help requested

goroutine 1 [running]:
flag.(*FlagSet).Parse(0xc000096120, 0xc000098010, 0x1, 0x1, 0xc000082f88, 0x100534f)
        /usr/local/Cellar/go/1.12.7/libexec/src/flag/flag.go:983 +0xf8
flag.Parse(...)
        /usr/local/Cellar/go/1.12.7/libexec/src/flag/flag.go:998
main.main()
        /Users/daixuan/pinduoduo/go/src/github.com/Golang_Puzzlers/src/puzzlers/article2/q1/demo2.go:21 +0x78
exit status 2

這是由於我們在這裏傳給flag.NewFlagSet函數的第二個參數值是flag.PanicOnError。flag.PanicOnError和flag.ExitOnError都是預定義在flag包中的常量。

flag.ExitOnError的含義是,告訴命令參數容器,當命令後跟--help或者參數設置的不正確的時候,在打印命令參數使用說明後以狀態碼2結束當前程序。

狀態碼2代表用戶錯誤地使用了命令,而flag.PanicOnError與之的區別是在最後拋出“運行時恐慌(panic)”。

上述兩種情況都會在我們調用flag.Parse函數時被觸發。順便提一句,“運行時恐慌”是 Go 程序錯誤處理方面的概念。關於它的拋出和恢復方法,我在本專欄的後續部分中會講到。


下面再進一步,我們索性不用全局的flag.CommandLine變量,轉而自己創建一個私有的命令參數容器。我們在函數外再添加一個變量聲明:

我們把對flag.StringVar的調用替換爲對cmdLine.StringVar調用,再把flag.Parse()替換爲cmdLine.Parse(os.Args[1:])。其中的os.Args[1:]指的就是我們給定的那些命令參數。這樣做就完全脫離了flag.CommandLine。*flag.FlagSet類型的變量cmdLine擁有很多有意思的方法

package main

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

var name string

// 方式3。
var cmdLine = flag.NewFlagSet("question", flag.ExitOnError)

func init() {
    // 方式3。
    cmdLine.StringVar(&name, "name", "everyone", "The greeting object.")
}

func main() {
    // 方式3。
    cmdLine.Parse(os.Args[1:])
    fmt.Printf("Hello, %s!\n", name)
}
go run demo3.go --help
Usage of question:
  -name string
        The greeting object. (default "everyone")
exit status 2

go run demo3.go --name="test"
Hello, test!


這樣做的好處依然是更靈活地定製命令參數容器。但更重要的是,你的定製完全不會影響到那個全局變量flag.CommandLine。


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