Go 用 cobra 构建企业级 CLI:子命令、flag 绑定与配置加载

test12026-10-190 次阅读

为什么手写 flag 不够

小工具用标准库 flag 包够用,但一旦要支持子命令(如 app serve / app migrate)、全局与局部 flag、自动 help 与补全,就得上 cobra。它是 Kubernetes、Hugo 等项目的 CLI 框架,约定清晰、扩展性强。

定义根命令与子命令

每个命令是一个 *cobra.Command,Run 里写逻辑。子命令用 AddCommand 挂到根上。

package cmd

import "github.com/spf13/cobra"

var rootCmd = &cobra.Command{
    Use:   "app",
    Short: "业务管理命令行工具",
}

var serveCmd = &cobra.Command{
    Use:   "serve",
    Short: "启动 HTTP 服务",
    RunE: func(cmd *cobra.Command, args []string) error {
        port, _ := cmd.Flags().GetInt("port")
        return startServer(port)
    },
}

func init() {
    serveCmd.Flags().Int("port", 8080, "监听端口")
    rootCmd.AddCommand(serveCmd)
}

func Execute() error { return rootCmd.Execute() }

用 viper 把 flag 与配置文件打通

cobra 的 flag 变化多,配合 viper 可以从环境变量、yaml 文件回填默认值,优先级是 flag > 环境 > 文件 > 代码默认。

func init() {
    serveCmd.Flags().String("config", "", "配置文件路径")
    serveCmd.PreRunE = func(cmd *cobra.Command, args []string) error {
        cfgFile, _ := cmd.Flags().GetString("config")
        if cfgFile != "" {
            viper.SetConfigFile(cfgFile)
            return viper.ReadInConfig()
        }
        return nil
    }
}

错误处理与退出码

用 RunE 返回 error,cobra 会自动打印并退出码非 0;需要自定义退出码时调用 os.Exit 并在 SilenceErrors 关掉默认输出。

var versionCmd = &cobra.Command{
    Use:   "version",
    Short: "打印版本",
    Run:   func(cmd *cobra.Command, args []string) { fmt.Println("v1.4.0") },
}

func main() {
    if err := cmd.Execute(); err != nil {
        os.Exit(1)
    }
}

落地要点

  • 子命令放独立文件、在各自 init 里 AddCommand,避免根文件膨胀。
  • 需要校验必填参数用 MarkFlagRequired,cobra 自动拦截并提示。
  • 持久 flag(如 --verbose)挂在 PersistentFlags,对所有子命令生效。
  • 发布前跑 app --help 和 app serve --help,确认 help 文案与默认值正确。
T

test1

文章作者

为什么手写 flag 不够 小工具用标准库 flag 包够用,但一旦要支持子命令(如 app serve / app...

分类
技术
发布时间
2026-10-19
字数
约 1673 字
阅读
0 次