golang flag包源碼閱讀

前言

用過golang的小伙伴應該都知道flag包,因此這邊就不做使用的介紹了衙熔。本文主要簡單分析flag包的源碼赶撰,以及簡單介紹下如何自定義自己的value類型蝌以。

本文測試代碼如下:

var kk *bool = flag.Bool("k",false,"just k")

func main() {????

????//flag.Usage()????

????flag.Parse()????????

????fmt.Println(*kk)

}

代碼分析

當我們 import? package時哪替,package內(nèi)的全局變量會進行初始化栋荸,并且init函數(shù)會執(zhí)行。對應flag包凭舶,代碼如下:

var CommandLine = NewFlagSet(os.Args[0], ExitOnError)

func init() {? ? ? ?

????CommandLine.Usage = commandLineUsage

}

CommandLine?代表的是當前可執(zhí)行程序的命令行集合晌块,也就是 FlagSet。

CommandLine.Usage從字面理解就是使用說明帅霜。

NewFlagSet函數(shù)用于構(gòu)造一個FlagSet匆背,這樣的函數(shù)命名也是golang推薦的。

對于FlagSet結(jié)構(gòu)體义屏,我們可以先看下它的定義:

type FlagSet struct {? ?

????Usage func()? ? ? ?

????name string? ? ? //FlagSet名字靠汁,被賦值為 os.Args[0]

????parsed bool? ? ?//是否被解析過

????actual map[string]*Flag? ? //在實際命令行中使用到的Flag

????formal map[string]*Flag? ? ?//在代碼中預定義的全部Flag

????args []string? ? ? ? ? ? ? ? ? ? ? //等于 os.Args[1:]

????errorHandling ErrorHandling????

????output io.Writer

}

注意從 Flag包中導出的函數(shù)最終都會調(diào)用到?CommandLine?對應的函數(shù)蜂大,所以這里我只分析?FlagSet?的對應函數(shù)闽铐。

Flag包初始化的過程之后,接下來就到測試代碼的分析:

var kk *bool = flag.Bool("k",false,"just k")

三個參數(shù)分別為?name,default value,usage奶浦。

func (f *FlagSet) Bool(name string, value bool, usage string) *bool {????

????p := new(bool)????

????f.BoolVar(p, name, value, usage)????

????return p

}

函數(shù)簡單明了兄墅,new出一個p,通過?FlagSet.BoolVar的處理澳叉,p會承接命令行中的值隙咸,然后直接返回p的指針沐悦。

所以最關(guān)鍵的函數(shù)在于?FlagSet.BoolVar?函數(shù)。

func (f *FlagSet) BoolVar(p *bool, name string, value bool, usage string) {????

????f.Var(newBoolValue(value, p), name, usage)

}

BoolVar?先調(diào)用?newBoolValue五督,之后調(diào)用?f.Var藏否。

func newBoolValue(val bool, p *bool) *boolValue{? ??

????*p = val????

????return (*boolValue)(p)

}

newBoolValue功能只有一個,把默認值賦給?p充包,并將p轉(zhuǎn)換成 boolValue指針類型副签,然后返回。

注意?boolValue?擁有成員函數(shù):?Set/String/Get基矮,因此boolValue實現(xiàn)了 flag.Value淆储,fmt.Stringer,flag.Getter接口家浇。

func (f *FlagSet) Var(value Value, name string, usage string) {? ?

? ? flag := &Flag{name, usage, value, value.String()}????

????_, alreadythere := f.formal[name]????

????if alreadythere {????????

????????var msg string????????

????????if f.name == "" {????????????

????????????msg = fmt.Sprintf("flag redefined: %s", name)????????

????????} else {????????????

????????????????????????msg = fmt.Sprintf("%s flag redefined: %s", f.name, name)????????

????????}????????

????????fmt.Fprintln(f.Output(), msg)????????

????????panic(msg)?

? ?}????

????if f.formal == nil {????????

????????f.formal = make(map[string]*Flag)????

????}????

????f.formal[name] = flag

}

FlagSet.Var函數(shù)首先新建一個?Flag結(jié)構(gòu)體本砰,然后檢查對應的?flag的name是否存在于 FlagSet的formal?slice中,如果存在同名flag钢悲,直接panic点额。

如果不存在則直接把flag存放在 formal中。

至此莺琳,預定義的flag已經(jīng)存放在?CommandLine?中咖楣。

之后我們分析?flag.Parse函數(shù)。

func (f *FlagSet) Parse(arguments []string) error {????

????f.parsed = true????

????f.args = arguments????

????for {????????

????????seen, err := f.parseOne()????????

? ? ????......

????}

}

其中?arguments? ? 等于 os.Args[1:]芦昔,因此Flag.Args()得到的是純正的命令行參數(shù)诱贿,不包括 os.Args[0]。但是要千萬注意咕缎,parseOne?函數(shù)會一直更新 FlagSet.args slice珠十,從而導致?Parse結(jié)束后,F(xiàn)lag.Args()?得到的是一個空的 slice凭豪。

Parse函數(shù)中焙蹭,最主要的函數(shù)是?FlagSet.ParseOne?函數(shù)。

ParseOne?的函數(shù)同樣很簡單嫂伞,解析 “-”?符號孔厉,根據(jù) “=”?的位置,解析出?name?與 value帖努,其中很關(guān)鍵的代碼:

fv, ok := flag.Value.(boolFlag)

fv.Set(value)

這里?Set 由Value接口定義撰豺,因此如果我們自定義參數(shù)類型的話,必須實現(xiàn)?Value?接口拼余。

函數(shù)最后?flag會被添加到?FlagSet.actual?中污桦。

基本的代碼分析到這,里面還有一些比較簡單的函數(shù)匙监,比如usage相關(guān)凡橱,arg相關(guān)的函數(shù)小作,可以自己看看。

自定義Flag

根據(jù)上面代碼的分析稼钩,只要我們定義的Flag結(jié)構(gòu)體實現(xiàn)了Value接口顾稀,即能通過命令行初始化。下面代碼為例:

type myFlag struct {????

????name string????

????age int

}

func (mf *myFlag) Set(data string) error {????

????b := bytes.NewBufferString(data)????

????fmt.Fscanf(b,"%d%s",&mf.age,&mf.name)????

????return nil

}

func (mf *myFlag)String() string {????

????return fmt.Sprintf("%s now is %d",mf.name,mf.age)

}

var mf *myFlag = &myFlag{}

func init() {????

????flag.CommandLine.Var(mf,"na","name age")

}

func main() {????

//flag.Usage()????

flag.Parse()????????

fmt.Println(mf)

}

在實現(xiàn)?Value接口后坝撑,主要需要調(diào)用?flag.CommandLine.Var 础拨,當然也可以調(diào)用 flag.Var?函數(shù)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末绍载,一起剝皮案震驚了整個濱河市诡宗,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌击儡,老刑警劉巖塔沃,帶你破解...
    沈念sama閱讀 218,451評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異阳谍,居然都是意外死亡蛀柴,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評論 3 394
  • 文/潘曉璐 我一進店門矫夯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鸽疾,“玉大人,你說我怎么就攤上這事训貌≈瓢梗” “怎么了?”我有些...
    開封第一講書人閱讀 164,782評論 0 354
  • 文/不壞的土叔 我叫張陵递沪,是天一觀的道長豺鼻。 經(jīng)常有香客問我,道長款慨,這世上最難降的妖魔是什么儒飒? 我笑而不...
    開封第一講書人閱讀 58,709評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮檩奠,結(jié)果婚禮上桩了,老公的妹妹穿的比我還像新娘。我一直安慰自己埠戳,他們只是感情好井誉,可當我...
    茶點故事閱讀 67,733評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著乞而,像睡著了一般送悔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上爪模,一...
    開封第一講書人閱讀 51,578評論 1 305
  • 那天欠啤,我揣著相機與錄音,去河邊找鬼屋灌。 笑死洁段,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的共郭。 我是一名探鬼主播祠丝,決...
    沈念sama閱讀 40,320評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼除嘹!你這毒婦竟也來了写半?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,241評論 0 276
  • 序言:老撾萬榮一對情侶失蹤尉咕,失蹤者是張志新(化名)和其女友劉穎叠蝇,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體年缎,經(jīng)...
    沈念sama閱讀 45,686評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡悔捶,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,878評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了单芜。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蜕该。...
    茶點故事閱讀 39,992評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖洲鸠,靈堂內(nèi)的尸體忽然破棺而出堂淡,到底是詐尸還是另有隱情,我是刑警寧澤扒腕,帶...
    沈念sama閱讀 35,715評論 5 346
  • 正文 年R本政府宣布淤齐,位于F島的核電站,受9級特大地震影響袜匿,放射性物質(zhì)發(fā)生泄漏更啄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,336評論 3 330
  • 文/蒙蒙 一居灯、第九天 我趴在偏房一處隱蔽的房頂上張望祭务。 院中可真熱鬧,春花似錦怪嫌、人聲如沸义锥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拌倍。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間柱恤,已是汗流浹背数初。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留梗顺,地道東北人泡孩。 一個月前我還...
    沈念sama閱讀 48,173評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像寺谤,于是被迫代替她去往敵國和親仑鸥。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,947評論 2 355

推薦閱讀更多精彩內(nèi)容