flag.string() 返回 *string 指针,需解引用获取值;flag.parse() 必须在所有 flag 定义之后调用,否则参数不可见;自定义类型需实现 flag.value 接口(set 和 string 方法),接收者须为指针;短选项与长选项独立注册,需手动合并逻辑。

flag.String() 返回的是指针,不是字符串值
直接把 flag.String() 的返回值当 string 用会编译失败;更隐蔽的问题是,在 flag.Parse() 前解引用,拿到的永远是零值(空字符串),而不是用户输入——因为此时还没解析。
-
flag.String("name", "default", "")返回*string,必须用*name才能取到值 - 如果变量生命周期短(比如定义在 if 分支里),
flag.Parse()后可能已失效,建议统一放在main()顶部声明 - 想避免指针管理?改用
flag.StringVar(&cfg.Name, "name", "default", ""),绑定已有字段更安全
自定义类型必须实现 flag.Value 接口才能被解析
想支持 --ports 8080,3000 这种逗号分隔格式?不能靠 flag.StringSlice 硬凑——它只按空格拆,且不处理重复 flag。必须自己实现 flag.Value。
- 接口只有两个方法:
Set(string) error和String() string,缺一就会 panic -
Set负责把字符串转成目标结构,比如用strings.Split(value, ",")并过滤空项 -
String返回 help 里显示的默认值,别返回空字符串,否则flag.PrintDefaults()不显示默认值 - 注意接收者用指针:
func (s *PortList) Set(...),否则无法修改原变量
flag.Parse() 必须在所有 flag 定义之后调用
这是最常静默失败的点:flag.Parse() 只解析**此前已注册**的 flag。定义晚了,参数就完全不可见,还不会报错。
- 错误写法:
flag.Parse()写在第一行,后面再flag.Int("port", 8080, "")→ 用户传-port 3000,*port还是 0 - 正确顺序:所有
flag.Xxx()或flag.XxxVar()先写完,再调一次flag.Parse() - 别在
init()里调flag.Parse()—— 此时os.Args可能未就绪,拿到空 slice - 子命令需隔离:用
flag.NewFlagSet()创建独立实例,各自调.Parse()
短选项和长选项不能自动共用同一变量
-h 和 --help 在标准 flag 包里是两个完全独立的 flag,不会映射、不会同步,也不会共享内存地址。
- 显式注册两个:
flag.Bool("h", false, "show help")和flag.Bool("help", false, "show help") - 它们各自绑定不同变量,业务逻辑里得手动合并:
if *h || *help { flag.Usage(); os.Exit(0) } - 高频短选项(如
-v)可以保留,但语义要清晰;复杂参数建议只用长选项,避免维护两套逻辑 - 真要别名支持?换
spf13/pflag,它兼容 POSIX 规范,且被cobra默认使用
String() 方法或调用顺序错,问题就藏得深。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











