Files
bulma/flag/subcommand_advanced.go

129 lines
3.8 KiB
Go

package main
import (
"flag"
"fmt"
"os"
)
// GlobalOptions 全局公共参数,所有子命令共享
type GlobalOptions struct {
Verbose bool // 详细日志模式
Config string // 全局配置文件
}
// SubCommand 通用子命令抽象结构体
type SubCommand struct {
Name string
Description string
Flags *flag.FlagSet
Run func([]string, GlobalOptions) error
}
// 提前声明变量,避免初始化循环引用
var commands map[string]*SubCommand
// init 初始化子命令,将 FlagSet 的绑定与执行逻辑分离
func init() {
// --- 初始化 build 命令 ---
buildCmd := &SubCommand{
Name: "build",
Description: "编译构建项目",
Flags: flag.NewFlagSet("build", flag.ContinueOnError),
}
var buildOutput string
// 在外部绑定参数,而不是在 Run 里面重新创建 FlagSet
buildCmd.Flags.StringVar(&buildOutput, "output", "./dist", "项目构建输出目录")
buildCmd.Run = func(args []string, global GlobalOptions) error {
if err := buildCmd.Flags.Parse(args); err != nil {
return err
}
fmt.Printf("[构建成功] 输出目录:%s,全局详细日志:%t,全局配置:%s\n",
buildOutput, global.Verbose, global.Config)
return nil
}
// --- 初始化 deploy 命令 ---
deployCmd := &SubCommand{
Name: "deploy",
Description: "部署项目到环境",
Flags: flag.NewFlagSet("deploy", flag.ContinueOnError),
}
var deployEnv string
deployCmd.Flags.StringVar(&deployEnv, "env", "dev", "部署环境 dev/test/prod")
deployCmd.Run = func(args []string, global GlobalOptions) error {
if err := deployCmd.Flags.Parse(args); err != nil {
return err
}
fmt.Printf("[部署成功] 目标环境:%s,全局配置文件:%s\n",
deployEnv, global.Config)
return nil
}
// 注册子命令
commands = map[string]*SubCommand{
"build": buildCmd,
"deploy": deployCmd,
}
}
func main() {
// 1. 初始化全局参数 FlagSet(独立隔离)
var global GlobalOptions
globalFlags := flag.NewFlagSet("global", flag.ContinueOnError)
globalFlags.BoolVar(&global.Verbose, "verbose", false, "启用详细日志输出")
globalFlags.StringVar(&global.Config, "config", "./config.yaml", "全局配置文件路径")
// 2. 解析全局参数
// 注意:flag 库遇到第一个非 Flag 参数(即子命令名称)就会停止解析
if err := globalFlags.Parse(os.Args[1:]); err != nil {
if err == flag.ErrHelp {
printHelp(globalFlags)
return
}
fmt.Println("全局参数解析失败:", err)
os.Exit(1)
}
// 3. 获取剩余参数(子命令 + 子命令参数)
remainingArgs := globalFlags.Args()
if len(remainingArgs) == 0 {
printHelp(globalFlags)
os.Exit(1)
}
// 4. 路由分发执行子命令
cmdName := remainingArgs[0]
if cmd, exists := commands[cmdName]; exists {
if err := cmd.Run(remainingArgs[1:], global); err != nil {
if err == flag.ErrHelp {
// 当子命令触发 -h 时,打印该子命令专属的帮助信息
fmt.Printf("\n===== 子命令 [%s] 帮助信息 =====\n", cmd.Name)
fmt.Printf("说明:%s\n\n用法:%s [选项]\n\n选项:\n", cmd.Description, cmd.Name)
cmd.Flags.PrintDefaults()
return
}
fmt.Printf("子命令执行失败:%v\n", err)
os.Exit(1)
}
} else {
fmt.Printf("❌ 未知子命令:%s\n", cmdName)
printHelp(globalFlags)
os.Exit(1)
}
}
// printHelp 统一打印全局帮助信息
func printHelp(globalFlags *flag.FlagSet) {
fmt.Println("===== 项目工程化 CLI 工具 =====")
fmt.Println("用法:tool [全局选项] <子命令> [子命令选项]")
fmt.Println("\n全局选项:")
globalFlags.PrintDefaults()
fmt.Println("\n可用子命令:")
for _, cmd := range commands {
fmt.Printf(" %-10s %s\n", cmd.Name, cmd.Description)
}
fmt.Println("\n提示:使用 'tool <子命令> -h' 查看子命令详细帮助")
}