flag.string、flag.int等必须在flag.parse()前调用,否则参数无法识别且访问会panic;自定义类型需实现flag.value接口;重写flag.usage时须显式调用flag.printdefaults();子命令应使用flag.newflagset避免冲突。
flag.String、flag.Int 等函数必须在 flag.Parse() 前调用
Go 的 flag 包是惰性绑定:声明参数时只是注册,真正从 os.Args 解析并赋值要等到 flag.Parse() 执行。如果在 flag.Parse() 之后再调用 flag.String(),该参数永远不会被识别,且后续访问会 panic(比如对 nil 指针解引用)。
常见错误写法:
port := flag.Int("port", 8080, "server port")
flag.Parse()
// ... 业务逻辑
flag.Bool("debug", false, "enable debug mode") // ❌ 错误:Parse 后不能再注册
正确顺序应为:
- 所有
flag.Xxx()调用放在最前面(通常紧贴main()开头) - 然后统一调用
flag.Parse() - 最后通过返回的指针或变量读取值
自定义类型需实现 flag.Value 接口才能用 flag.Var
当需要支持复杂参数类型(如逗号分隔的字符串切片、自定义结构体、时间范围等),不能直接用 flag.StringSlice 硬凑,而应实现 flag.Value 接口(含 Set(string) error 和 String() string)。否则解析失败时只会静默忽略或报 “invalid value” 错误。
例如支持 -tags=a,b,c:
type tagsValue []string
func (t *tagsValue) Set(s string) error {
*t = strings.Split(s, ",")
return nil
}
func (t *tagsValue) String() string { return strings.Join(*t, ",") }
var tags tagsValue
flag.Var(&tags, "tags", "comma-separated list of tags")
注意点: