Package flag implements CLI flag parsing.
Specifications
0. Intro
Let’s start with a simple example:
1package main2
3import (4 "flag"5 "fmt"6 "time"7)8
9// 1. Define flag varaibles10var (11 host string12 port int13 isTestMode bool14 timeout time.Duration15)16 collapsed lines
16
17func main() {18 // 2. Bind flags to varaibles19 flag.StringVar(&host, "host", "localhost", "db host")20 flag.IntVar(&port, "port", 3306, "db port")21 flag.BoolVar(&isTestMode, "isTest", true, "isTestMode")22 flag.DurationVar(&timeout, "timeout", 5*time.Second, "db timeout")23
24 //3. Parse he command line into the defined flags25 flag.Parse()26
27 fmt.Println("host:", host)28 fmt.Println("port:", port)29 fmt.Println("isTestMode:", isTestMode)30 fmt.Println("timeout:", timeout)31}
output:
1❯ go run main.go2host: localhost3port: 33064isTestMode: true5timeout: 5s6
7❯ go run main.go -port 4000 -timeout 30s8host: localhost9port: 400010isTestMode: true11timeout: 30s
2. Procedures
- Define flag varaibles
- Bind flags to varaibles
- Parse he command line into the defined flags using
flag.Parse()
3. Command line flag syntax
The following forms are allowed:
-flag
or--flag
: only for boolean flags.-flag=x
: use for all types-flag x
: for non-boolean flags only
1// isProd is in default true in this example:2❯ go run main.go --isProd false3isProdMode: true4❯ go run main.go --isProd=false5isProdMode: false
4. Two types of functions
flag.typeVar
v.s. flag.type
1func StringVar(p *string, name string, value string, usage string)2func BoolVar(p *bool, name string, value bool, usage string)3func IntVar(p *int, name string, value int, usage string)4func DurationVar(p *time.Duration, name string, value time.Duration, usage string)5
6func String(name string, value string, usage string) *string7func Bool(name string, value bool, usage string) *bool8func Int(name string, value int, usage string) *int9func Duration(name string, value time.Duration, usage string) *time.Duration
1var x int2flag.IntVar(x, "x", 10, "define x")3
4// flag.type5var y = flag.Int("y", 20, "define y")
5. Custom type
If we want to define custom type for CLI, we need to implement the Value
interface for the new type:
1type Value interface {2 String() string // print value3 Set(string) error // set flag value4}5
6type ClusterArray []int7
8func (arr *ClusterArray) Set(val string) error {9 // disable the flag to be set multiple times10 if len(*arr) > 0 {11 return errors.New("cluster flag already set")12 }13 // type convertion14 for _, val := range strings.Split(val, ",") {15 cluster, err := strconv.Atoi(val)23 collapsed lines
16 if err != nil {17 return err18 }19 *arr = append(*arr, cluster)20 }21 return nil22}23
24func (arrs *ClusterArray) String() string {25 str := "["26 for _, s := range *arrs {27 str += strconv.Itoa(s)28 }29 str += "]"30 return str31}32
33func main() {34 var clusters ClusterArray35 flag.Var(&clusters, "clusters", "db clusters")36 flag.Parse()37 fmt.Println("clusters:", clusters)38}
output:
1❯ go run main.go -clusters=1,4,62clusters: [1,4,6]3
4❯ go run main.go -clusters=1,2 -clusters=55invalid value "5" for flag -clusters: cluster flag already set6
7❯ go run main.go -clusters=1,2,i8
9invalid value "1,2,i" for flag -clusters: strconv.Atoi: parsing "i": invalid syntax
6. Shorthand
1func main() {2 var port int3 flag.IntVar(&port, "p", 3306, "db port (shorthand)")4 flag.IntVar(&port, "port", 3306, "db port")5 flag.parse()6 fmt.Println("port:", port)7}
output:
1❯ go run main.go -p 10002port: 10003❯ go run main.go -port 10004port: 1000
You might find the complete example of codes in this repo: go-playground