golang flag

The Go language is said to be “battery-included”, which means that the Go standard library is available out of the box, providing Gopher with a feature-rich set of common toolkits that are sufficient for most everyday development needs. In particular, the Go Standard Library toolkit is widely used in areas where the Go language excels. The following chart shows the results of the Official Go 2020 User Survey.

Official Go 2020 User Survey

We see that cli (command-line interface) domain development occupies the Top2 position for Go language applications, second only to developing API/RPC services. The development of common cli applications is always inseparable from the flag package of the standard library.

flag package is estimated to be the must go through for many gopher to start go language. flag is very easy to use and not bad, it supports the flag form of regular command line programs, such as the following sample program.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// flag_demo1.go
package main

import (
    "flag"
    "fmt"
)

var (
    n = flag.Int("n", 1234, "help message for flag n")
)

func main() {
    flag.Parse()
    fmt.Printf("n=%d\n", *n)
}

flag_demo1 supports only one cmd flag: -n. We can use the flag_demo1 cli program to pass a value for the variable n like the following.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
$go build flag_demo1.go

$./flag_demo1
n=1234 //默认值

$./flag_demo1 -n 1111
n=1111

$./flag_demo1 --n 1111
n=1111 // --n和-n是等价的

$./flag_demo1 -n=2222
n=2222

$./flag_demo1 --n=2222
n=2222

We see that we can pass parameters to an integer flag variable using the following four forms.

  • -n value
  • -n value
  • -n=value
  • -n=value

Whichever form is used, they have an equivalent effect.

But when we place the flag at the end of the cli application, we have to be careful.

1
2
$./flag_demo1 show -n=2222
n=1234

We see that although we pass -n=2222 in the parameter list of flag_demo1 on the command line, the flag package of flag_demo1 directly ignores this parameter pass and sets the variable n to the default value of 1234.

This is because the parsing logic of the command line parameters of the flag package is such that parsing stops when the first non-flag parameter is encountered. The “show” passed to the above command line is not a flag parameter of flag_demo1, so the flag package will stop parsing the next command line parameter (-n=2222) after parsing the show, so the above command line is equivalent to

1
2
$./flag_demo1 show
n=1234

Then it’s not surprising that n=1234! This is what I call the first “little trap” of the flag package. Not only can non-flag parameters like “show” block the flag package from parsing the list of command-line arguments, but the separate “-” and “- " also have the same “blocking function”.

1
2
3
4
$./flag_demo1 -- -n=2222
n=1234
$./flag_demo1 - -n=2222
n=1234

We also often use bool class parameter values in command line flag parameters, such as the following example.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// flag_demo2.go

package main

import (
    "flag"
    "fmt"
)

var (
    n  = flag.Int("n", 1234, "int value for flag n")
    b1 = flag.Bool("b1", false, "bool value for flag b1")
    b2 = flag.Bool("b2", false, "bool value for flag b2")
)

func main() {
    flag.Parse()
    fmt.Printf("n=%d\n", *n)
    fmt.Printf("b1=%t\n", *b1)
    fmt.Printf("b2=%t\n", *b2)
}

This example has two bool flag parameters and one int flag parameter, so let’s run the cli application.

1
2
3
4
5
$go build flag_demo2.go
$./flag_demo2 -b1 true -b2 true -n 2222
n=1234
b1=true
b2=false

The output of the run seems to be different from the expected result! Why is the value of the b2 variable still false and why is the value of the n variable not 2222?

The problem lies in the special nature of bool type flag parameters. For some reason, bool type flag parameters do not support the “-arg value” form, but only the following two forms.

1
2
-arg
-arg=value

Let’s run the above flag_demo2 again with the correct method of passing flag parameters of type bool.

1
2
3
4
$./flag_demo2 -b1=true -b2=true -n 2222
n=2222
b1=true
b2=true

The output this time matches the expectation.

However, those who are careful may notice that the previous incorrect usage of

1
$./flag_demo2 -b1 true -b2 true -n 2222

Very confusing! Because the output value of variable b1 is as expected, this gives the false impression that the flag argument is being passed correctly. This “illusion” allows gopher to unwittingly fall into the second “trap” of the flag package. The incorrect flag argument value passing above is essentially equivalent to.

1
$./flag_demo2 -b1

That’s why b1=true and b2 and n are the default values!

The flag package is one of the most widely used standard library packages in our daily life, so be sure to understand the possible misuse of the flag package and don’t fall into the “little trap” of the flag package! We hope that the information shared here will help you to bypass these “traps” in your daily life!