1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Golang開(kāi)發(fā)命令行工具之flag包的使用

        共 6144字,需瀏覽 13分鐘

         ·

        2021-10-16 14:15

        目錄

        • 1、命令行工具概述

        • 2、flag 包介紹

        • 3、flag 包命令行參數(shù)的定義

        • 4、flag 包命令行參數(shù)解析

        • 5、flag 包命令行幫助

        • 6、flag 定義短參數(shù)和長(zhǎng)參數(shù)

        • 7、示例


        1、命令行工具概述

        日常命令行操作,相對(duì)應(yīng)的眾多命令行工具是提高生產(chǎn)力的必備工具,我在之前的文章?我的生產(chǎn)力工具推薦-終端01篇?[1]中有推薦過(guò)一些我常用的基于terminal終端的命令行cli工具

        鼠標(biāo)能夠讓用戶更容易上手,降低用戶學(xué)習(xí)成本。而對(duì)于開(kāi)發(fā)者,鍵盤(pán)操作模式能顯著提升生產(chǎn)力,還有在一些專業(yè)工具中, 大量使用快捷鍵代替繁瑣的鼠標(biāo)操作,能夠使開(kāi)發(fā)人員更加專注于工作,提高效率,因?yàn)殒I盤(pán)操作模式更容易產(chǎn)生肌肉記憶

        舉個(gè)栗子。我司業(yè)務(wù)研發(fā),前些年在我們的強(qiáng)力推動(dòng)下(被迫)轉(zhuǎn)向使用了git作為版本控制,開(kāi)始使用的是圖形化“小烏龜”工具。后續(xù)出現(xiàn)幾次問(wèn)題解決起來(lái)較麻煩后,推薦其使用原生的git命令行。如今,使用git命令行操作版本控制可謂 “一頓操作猛如虎......”

        命令行(鍵盤(pán))操作在很大程度上可以提高工作效率,與之相對(duì)應(yīng)的是鼠標(biāo)(觸屏等)操作,這兩種模式是目前的主流人機(jī)交互方式

        設(shè)計(jì)一款命令行工具的開(kāi)發(fā)語(yǔ)言可以選擇原始的shell、甚至是更原始的語(yǔ)言C,更為容易上手且功能更多的有node、pythongolang

        本文是基于golang開(kāi)發(fā)命令行工具的開(kāi)篇,主要是基于golang原生內(nèi)置的、輕量的flag包實(shí)現(xiàn),用golang設(shè)計(jì)命令行工具而不用shell、python的原因這里就不做論述了

        2、flag 包介紹

        flag包用來(lái)解析命令行參數(shù)

        相比簡(jiǎn)單的使用os.Args來(lái)獲取命令行參數(shù),flag可以實(shí)現(xiàn)按照更為通用的命令行用法,例如mysql -u root -p 123456。其中mysql是命令行的名稱即這個(gè)命令,-u-p分別是這個(gè)命令的兩個(gè)參數(shù):用戶名和密碼,后面接著的是對(duì)應(yīng)的參數(shù)值,有了參數(shù)的聲明之后,兩個(gè)參數(shù)可以互換位置,參數(shù)值也可以選填或按照缺省(默認(rèn))值進(jìn)行指定

        flag的詳細(xì)用法可參考flag 包文檔[2]

        flag包支持的命令行參數(shù)的類型有bool、int、int64、uint、uint64、float float64、string、duration

        即布爾值、整型、浮點(diǎn)型、字符串、時(shí)間段類型

        3、flag 包命令行參數(shù)的定義

        定義flag命令行參數(shù),用來(lái)接收命令行輸入的參數(shù)值,一般有以下兩種方法

        • flag.TypeVar():先定義參數(shù)(實(shí)際上是指針),再定義flag.TypeVar將命令行參數(shù)存儲(chǔ)(綁定)到前面參數(shù)的值的指針(地址)
        var?name?string
        var?age?int
        var?height?float64
        var?graduated?bool
        //?&name?就是接收用戶命令行中輸入的-n后面的參數(shù)值
        //?返回值是一個(gè)用來(lái)存儲(chǔ)name參數(shù)的值的指針/地址
        //?定義string類型命令行參數(shù)name,括號(hào)中依次是變量名、flag參數(shù)名、默認(rèn)值、參數(shù)說(shuō)明
        flag.StringVar(&name,?"n",?"",?"name參數(shù),默認(rèn)為空")
        //?定義整型命令行參數(shù)age
        flag.IntVar(&age,"a",?0,?"age參數(shù),默認(rèn)為0")
        //?定義浮點(diǎn)型命令行參數(shù)height
        flag.Float64Var(&height,"h",?0,?"height參數(shù),默認(rèn)為0")
        //?定義布爾型命令行參數(shù)graduated
        flag.BoolVar(&graduated,"g",?false,?"graduated參數(shù),默認(rèn)為false")
        • flag.Type():用短變量聲明的方式定義參數(shù)類型及變量名
        //?定義string類型命令行參數(shù)name,括號(hào)中依次是flag參數(shù)名、默認(rèn)值、參數(shù)說(shuō)明
        namePtr?:=?flag.String("n",?"",?"name參數(shù),默認(rèn)為空")
        //?定義整型命令行參數(shù)age
        age?:=?flag.Int("a",?0,?"age參數(shù),默認(rèn)為0")
        //?定義浮點(diǎn)型命令行參數(shù)height
        height?:=?flag.Float64("h",?0,?"height參數(shù),默認(rèn)為0")
        //?定義布爾型命令行參數(shù)graduated
        graduated:=?flag.Bool("g",?false,?"graduated參數(shù),默認(rèn)為false")

        4、flag 包命令行參數(shù)解析

        固定用法,定義好參數(shù)后,通過(guò)調(diào)用flag.Parse()來(lái)對(duì)命令行參數(shù)進(jìn)行解析寫(xiě)入注冊(cè)的flag里,進(jìn)而解析獲取參數(shù)值,通過(guò)查看源碼中也是調(diào)用的os.Args

        源碼路徑go/src/flag/flag.go

        //?Parse?parses?the?command-line?flags?from?os.Args[1:].?Must?be?called
        //?after?all?flags?are?defined?and?before?flags?are?accessed?by?the?program.
        func?Parse()?{
        ?//?Ignore?errors;?CommandLine?is?set?for?ExitOnError.
        ?CommandLine.Parse(os.Args[1:])
        }

        進(jìn)而查看Parse方法的源碼

        func?(f?*FlagSet)?Parse(arguments?[]string)?error?{
        ?f.parsed?=?true
        ?f.args?=?arguments
        ?for?{
        ??seen,?err?:=?f.parseOne()
        ??if?seen?{
        ???continue
        ??}
        ??if?err?==?nil?{
        ???break
        ??}
        ??switch?f.errorHandling?{
        ??case?ContinueOnError:
        ???return?err
        ??case?ExitOnError:
        ???if?err?==?ErrHelp?{
        ????os.Exit(0)
        ???}
        ???os.Exit(2)
        ??case?PanicOnError:
        ???panic(err)
        ??}
        ?}
        ?return?nil
        }

        真正解析參數(shù)的是parseOne方法(這里省略源碼),結(jié)論是

        • 當(dāng)遇到單獨(dú)的一個(gè) "-" 或不是 "-" 開(kāi)始時(shí),會(huì)停止解析
        • 遇到連續(xù)的兩個(gè) "-" 時(shí),解析停止
        • 在終止符"-"之后停止

        解析參數(shù)時(shí),對(duì)于參數(shù)的指定方式一般有"-"、"--"、以及是否空格等方式,組合下來(lái)有如下幾種方式

        -flag xxx空格和一個(gè)-符號(hào)
        --flag xxx空格和兩個(gè)-符號(hào)
        -flag=xxx等號(hào)和一個(gè)-符號(hào)
        --flag=xxx等號(hào)和兩個(gè)-符號(hào)

        其中,-flag xxx方式最為常用,如果參數(shù)是布爾型,只能用等號(hào)方式指定

        5、flag 包命令行幫助

        flag包默認(rèn)會(huì)根據(jù)定義的命令行參數(shù),在使用時(shí)如果不輸入?yún)?shù)就打印對(duì)應(yīng)的幫助信息

        這樣的幫助信息我們可以對(duì)其進(jìn)行覆蓋去改變默認(rèn)的Usage

        package?main

        import?(
        ????"flag"
        ????"fmt"
        )

        func?main()??{
        ????var?host?string
        ????var?port?int
        ????var?verbor?bool
        ????var?help?bool
        ????//?綁定命令行參數(shù)與變量關(guān)系
        ????flag.StringVar(&host,?"H",?"127.0.0.1",?"ssh?host")
        ????flag.IntVar(&port,?"P",?22,?"ssh?port")
        ????flag.BoolVar(&verbor,?"v",?false,?"detail?log")
        ????flag.BoolVar(&help,?"h",?false,?"help")
        ????//?自定義-h
        ????flag.Usage?=?func()?{
        ????????fmt.Println(`
        Usage:?flag?[-H?addr]?[-p?port]?[-v]

        Options:
        ????`
        )
        ????????flag.PrintDefaults()
        ????}
        ????//?解析命令行參數(shù)
        ????flag.Parse()
        ????if?help?{
        ????????flag.Usage()
        ????}?else?{
        ????????fmt.Println(host,?port,?verbor)
        ????}
        }
        /*
        ???go?run?flag_args.go?-h

        Usage:?flag?[-H?addr]?[-p?port]?[-v]

        Options:

        ??-H?string
        ????????ssh?host?(default?"127.0.0.1")
        ??-P?int
        ????????ssh?port?(default?22)
        ??-h????help
        ??-v????detail?log
        ?*/

        6、flag 定義短參數(shù)和長(zhǎng)參數(shù)

        簡(jiǎn)單來(lái)說(shuō),短參數(shù)和長(zhǎng)參數(shù),就是例如我們?cè)谑褂媚承┟顣r(shí),查看命令版本可以輸入-V,也可以輸入--version。這種情況下,flag并沒(méi)有默認(rèn)支持,但是可以通過(guò)可以兩個(gè)選項(xiàng)共享同一個(gè)變量來(lái)實(shí)現(xiàn),即通過(guò)給某個(gè)相同的變量設(shè)置不同的選項(xiàng),參數(shù)在初始化的時(shí)候其順序是不固定的,因此還需要保證其擁有相同的默認(rèn)值

        package?main

        import?(
        ??"fmt"
        ??"flag"
        )

        var?logLevel?string

        func?init()?{
        ??const?(
        ????defaultLogLevel?=?"DEBUG"
        ????usage?=?"set?log?level"
        ??)
        ??flag.StringVar(&logLevel,?"log_level",?defaultLogLevel,?usage)
        ??flag.StringVar(&logLevel,?"l",?defaultLogLevel,?usage?+?"(shorthand)")
        }

        func?main()?{
        ??flag.Parse()
        ??fmt.Println("log?level:",?logLevel)
        }

        通過(guò)const聲明公共的常量,并在默認(rèn)值以及幫助信息中去使用,這樣就可以實(shí)現(xiàn)了

        7、示例

        實(shí)現(xiàn)計(jì)算字符串或目錄下遞歸計(jì)算文件md5的命令,類似linuxmd5sum命令

        其中利用bufio分批次讀取文件,防止文件過(guò)大時(shí)造成資源占用高

        package?main

        import?(
        ?"bufio"
        ?"crypto/md5"
        ?"flag"
        ?"fmt"
        ?"io"
        ?"os"
        ?"strings"
        )

        func?md5reader(reader?*bufio.Reader)?string?{??//
        ?hasher?:=?md5.New()??//?定義MD5?hash計(jì)算器
        ?bytes?:=?make([]byte,?1024*1024*10)??//?分批次讀取文件

        ?for?{
        ??n,?err?:=?reader.Read(bytes)
        ??if?err?!=?nil?{
        ???if?err?!=?io.EOF?{
        ????return?""
        ???}
        ???break
        ??}?else?{
        ???hasher.Write(bytes[:n])
        ??}
        ?}
        ?return?fmt.Sprintf("%x",?hasher.Sum(nil))
        }

        func?md5file(path?string)?(string,?error)?{
        ?file,?err?:=?os.Open(path)
        ?if?err?!=?nil?{
        ??return?"",?err
        ?}?else?{
        ??defer?file.Close()
        ??return?md5reader(bufio.NewReader(file)),?nil
        ?}
        }

        func?md5str(txt?string)?(string,?error)?{
        ?return?md5reader(bufio.NewReader(strings.NewReader(txt))),?nil
        ?//return?fmt.Sprintf("%x",?md5.Sum([]byte(txt)))
        }

        func?main()??{
        ?txt?:=?flag.String("s",?"",?"md5?txt")
        ?path?:=?flag.String("f",?"",?"file?path")
        ?help?:=?flag.Bool("h",?false,?"help")
        ?flag.Usage?=?func()?{
        ??fmt.Println(`
        Usage:?md5?[-s?123abc]?[-f?path]
        Options:
        ??`
        )
        ??flag.PrintDefaults()
        ?}
        ?flag.Parse()
        ?if?*help?||?*txt?==?""?&&?*path?==?""?{
        ??flag.Usage()
        ?}?else?{
        ??var?md5?string
        ??var?err?error
        ??if?*path?!=?""?{
        ???md5,?err?=?md5file(*path)
        ??}?else?{
        ???md5,?err?=?md5str(*txt)
        ??}
        ??if?err?!=?nil?{
        ???fmt.Println(err)
        ??}?else?{
        ???fmt.Println(md5)
        ??}
        ?}
        }

        編譯生成二進(jìn)制文件

        ???go?build?-o?md5go?-x?md5_bufio.go
        ???ll?md5go
        -rwxr-xr-x??1?ssgeek??staff???1.9M?Oct?2?00:54?md5go

        測(cè)試使用

        ???./md5go?-h

        Usage:?md5?[-s?123abc]?[-f?path]
        Options:

        ??-f?string
        ????????file?path
        ??-h????help
        ??-s?string
        ????????md5?txt
        ???./md5go?-s?123456
        e10adc3949ba59abbe56e057f20f883e
        ???./md5go?-f?md5_bufio.go
        8607a07cbb98cec0e9abe14b0db0bee6


        Just here,see you ~

        參考資料

        [1]

        https://www.ssgeek.com/post/wo-de-sheng-chan-li-gong-ju-tui-jian-zhong-duan-01-pian/

        [2]

        flag: https://pkg.go.dev/flag


        ? 歡迎進(jìn)群一起進(jìn)行技術(shù)交流

        ? 加群方式:公眾號(hào)消息私信“加群或加我好友再加群均可

        瀏覽 71
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            男女爽爽爽| 美女福利视频网 | 小早川怜子一区二区 | 精品蜜桃秘 一区二区三区观看 | 亚洲男人电影天堂 | 4438亚洲| 精品无码国产一区二区深花 | 欧美性少妇videosex | 羽月希被黑人吃奶dsd585 | 美女被草网站 |