golang flag库详解
flag
是 Go 标准库中用于命令行参数解析的库,适合用于 CLI 程序。它提供了简洁的接口来定义、解析和使用命令行标志(flags)。
一、基本使用
Go 中的命令行参数通常以如下格式传递:
./app -port=8080 -mode=debug
对应代码:
package main
import (
"flag"
"fmt"
)
func main() {
port := flag.Int("port", 8080, "端口号")
mode := flag.String("mode", "release", "运行模式")
flag.Parse() // 解析命令行参数
fmt.Println("port:", *port)
fmt.Println("mode:", *mode)
}
输出
$ go run main.go -port=9090 -mode=debug
port: 9090
mode: debug
二、支持的数据类型
类型函数 | 返回类型 | 示例 |
---|---|---|
flag.Bool | *bool | debug := flag.Bool("debug", false, "是否开启调试") |
flag.Int | *int | port := flag.Int("port", 8080, "端口号") |
flag.Int64 | *int64 | |
flag.Uint | *uint | |
flag.String | *string | mode := flag.String("mode", "dev", "模式") |
flag.Float64 | *float64 | |
flag.Duration | *time.Duration | 支持如 1s 、500ms 等格式 |
三、自定义变量(实现 flag.Value
接口)
type stringList []string
func (s *stringList) String() string {
return fmt.Sprintf("%v", *s)
}
func (s *stringList) Set(value string) error {
*s = append(*s, value)
return nil
}
func main() {
var names stringList
flag.Var(&names, "name", "指定名称,可多次设置")
flag.Parse()
fmt.Println("names:", names)
}
调用方式:
$ go run main.go -name=张三 -name=李四
names: [张三 李四]
四、flag.Parse()
、flag.Args()
和 flag.NArg()
flag.Parse() // 解析命令行参数
flag.Args() // 返回非 flag 参数(如命令后面跟的文件名)
flag.NArg() // 返回 Args() 的数量
flag.NFlag() // 返回设置了多少 flag
示例:
$ go run main.go -port=8080 config.yaml
flag.Args() => [config.yaml]
flag.NArg() => 1
flag.NFlag() => 1
五、flag 默认使用方式和 FlagSet
默认 FlagSet(flag.CommandLine
)
一般用 flag.Int
, flag.String
等就是在 flag.CommandLine
上注册的。
自定义 FlagSet(适用于多个子命令)
fs := flag.NewFlagSet("server", flag.ExitOnError)
port := fs.Int("port", 8000, "端口")
fs.Parse(os.Args[2:])
适合像:
$ myapp server -port=8000
$ myapp client -host=localhost
六、帮助信息自动生成
只需在代码中定义 flag,然后在终端中执行:
$ go run main.go -h
会自动打印每个 flag 的说明。
七、常见问题
- flag 必须在
flag.Parse()
前定义。 - 不能支持
-abc
连写,需要-a -b -c
分开写。 flag
不支持 POSIX 风格子命令解析(如git commit -m
),建议使用spf13/cobra
等库。
下面是一个实际项目中使用 flag
管理配置的完整示例,用于模拟一个简单的 Web 服务器程序。该程序支持如下配置参数:
-port
:监听端口(默认8080
)-mode
:运行模式,支持debug
/release
-timeout
:请求超时时间(默认5s
)-static
:静态资源目录(默认./static
)
✅ 示例:main.go
package main
import (
"flag"
"fmt"
"log"
"net/http"
"os"
"time"
)
type Config struct {
Port int
Mode string
Timeout time.Duration
StaticDir string
}
func parseFlags() *Config {
port := flag.Int("port", 8080, "监听端口")
mode := flag.String("mode", "release", "运行模式:debug/release")
timeout := flag.Duration("timeout", 5*time.Second, "请求超时时间,示例:3s, 500ms")
static := flag.String("static", "./static", "静态资源目录")
flag.Parse()
return &Config{
Port: *port,
Mode: *mode,
Timeout: *timeout,
StaticDir: *static,
}
}
func main() {
config := parseFlags()
if config.Mode != "debug" && config.Mode != "release" {
fmt.Println("mode 参数只能为 debug 或 release")
os.Exit(1)
}
if config.Mode == "debug" {
log.Printf("[调试模式] 当前配置: %+v\n", config)
}
mux := http.NewServeMux()
// 静态资源处理
fs := http.FileServer(http.Dir(config.StaticDir))
mux.Handle("/", fs)
// 创建带超时的服务器
server := &http.Server{
Addr: fmt.Sprintf(":%d", config.Port),
Handler: mux,
ReadTimeout: config.Timeout,
WriteTimeout: config.Timeout,
}
log.Printf("服务器启动中,监听端口:%d,模式:%s,静态目录:%s\n", config.Port, config.Mode, config.StaticDir)
if err := server.ListenAndServe(); err != nil {
log.Fatal("服务器启动失败:", err)
}
}
✅ 运行示例
# 默认运行
$ go run main.go
# 指定端口和运行模式
$ go run main.go -port=9090 -mode=debug -timeout=10s -static=./public
输出:
[调试模式] 当前配置: &{Port:9090 Mode:debug Timeout:10s StaticDir:./public}
服务器启动中,监听端口:9090,模式:debug,静态目录:./public
✅ 配合系统服务使用
你还可以将其编译后作为系统后台服务使用:
$ go build -o myserver main.go
$ ./myserver -port=8081 -mode=release -timeout=2s
这个结构清晰、可扩展,如果你后期打算支持子命令、配置文件等,可以在此基础上引入 flag.FlagSet
或者 viper
/ cobra
等。