Skip to content

Cobra — CLI 框架

Cobra 是 Go 最流行的 CLI 框架,Kubernetes、Hugo、GitHub CLI 都用它构建。

安装

bash
go get -u github.com/spf13/cobra@latest
# 可选:安装代码生成器
go install github.com/spf13/cobra-cli@latest

快速开始

go
package main

import (
    "fmt"
    "os"
    "github.com/spf13/cobra"
)

var rootCmd = &cobra.Command{
    Use:   "myapp",
    Short: "我的命令行工具",
    Long:  `一个功能强大的命令行工具,用于处理各种任务。`,
}

func main() {
    if err := rootCmd.Execute(); err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
}

子命令

go
// cmd/serve.go
var serveCmd = &cobra.Command{
    Use:   "serve",
    Short: "启动 HTTP 服务器",
    Long:  `启动一个 HTTP 服务器,监听指定端口。`,
    RunE: func(cmd *cobra.Command, args []string) error {
        port, _ := cmd.Flags().GetInt("port")
        fmt.Printf("服务器启动在端口 %d\n", port)
        return startServer(port)
    },
}

func init() {
    rootCmd.AddCommand(serveCmd)
    serveCmd.Flags().IntP("port", "p", 8080, "监听端口")
    serveCmd.Flags().String("host", "0.0.0.0", "监听地址")
}

// cmd/user.go — 嵌套子命令
var userCmd = &cobra.Command{
    Use:   "user",
    Short: "用户管理",
}

var userCreateCmd = &cobra.Command{
    Use:   "create [name]",
    Short: "创建用户",
    Args:  cobra.ExactArgs(1),  // 必须有且只有 1 个参数
    RunE: func(cmd *cobra.Command, args []string) error {
        name := args[0]
        email, _ := cmd.Flags().GetString("email")
        return createUser(name, email)
    },
}

var userListCmd = &cobra.Command{
    Use:   "list",
    Short: "列出所有用户",
    RunE: func(cmd *cobra.Command, args []string) error {
        page, _ := cmd.Flags().GetInt("page")
        return listUsers(page)
    },
}

func init() {
    rootCmd.AddCommand(userCmd)
    userCmd.AddCommand(userCreateCmd, userListCmd)

    userCreateCmd.Flags().StringP("email", "e", "", "用户邮箱(必填)")
    userCreateCmd.MarkFlagRequired("email")

    userListCmd.Flags().IntP("page", "p", 1, "页码")
}

// 使用:
// myapp user create Alice --email alice@example.com
// myapp user list --page 2

Flags 类型

go
// 持久 Flag(对所有子命令生效)
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "配置文件路径")
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "详细输出")

// 本地 Flag(只对当前命令生效)
serveCmd.Flags().IntP("port", "p", 8080, "端口")
serveCmd.Flags().StringSlice("tags", []string{}, "标签列表")
serveCmd.Flags().Duration("timeout", 30*time.Second, "超时时间")

// 必填 Flag
serveCmd.MarkFlagRequired("port")

// 互斥 Flag
serveCmd.MarkFlagsMutuallyExclusive("json", "yaml")

// 绑定到变量
var port int
serveCmd.Flags().IntVarP(&port, "port", "p", 8080, "端口")

与 Viper 集成

go
import (
    "github.com/spf13/cobra"
    "github.com/spf13/viper"
)

func init() {
    cobra.OnInitialize(initConfig)

    rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "配置文件")
    rootCmd.PersistentFlags().String("db-url", "", "数据库连接串")

    // 将 flag 绑定到 viper
    viper.BindPFlag("database.url", rootCmd.PersistentFlags().Lookup("db-url"))
}

func initConfig() {
    if cfgFile != "" {
        viper.SetConfigFile(cfgFile)
    } else {
        viper.SetConfigName("config")
        viper.SetConfigType("yaml")
        viper.AddConfigPath(".")
        viper.AddConfigPath("$HOME/.myapp")
    }

    viper.AutomaticEnv()  // 自动读取环境变量

    if err := viper.ReadInConfig(); err == nil {
        fmt.Println("使用配置文件:", viper.ConfigFileUsed())
    }
}

完整 CLI 项目结构

mycli/
├── cmd/
│   ├── root.go      # 根命令
│   ├── serve.go     # serve 子命令
│   ├── user.go      # user 子命令组
│   └── version.go   # version 子命令
├── internal/
│   └── ...
├── main.go
└── go.mod
go
// main.go
package main

import "mycli/cmd"

func main() {
    cmd.Execute()
}

// cmd/root.go
package cmd

import (
    "fmt"
    "os"
    "github.com/spf13/cobra"
)

var rootCmd = &cobra.Command{
    Use:   "mycli",
    Short: "我的 CLI 工具",
    // PersistentPreRunE 在所有子命令前执行
    PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
        return initializeApp()
    },
}

func Execute() {
    if err := rootCmd.Execute(); err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
}

// cmd/version.go
var versionCmd = &cobra.Command{
    Use:   "version",
    Short: "显示版本信息",
    Run: func(cmd *cobra.Command, args []string) {
        fmt.Printf("mycli version %s (commit: %s)\n", Version, Commit)
    },
}

func init() {
    rootCmd.AddCommand(versionCmd)
}

最佳实践

  • 使用 RunE 而非 Run,可以返回错误
  • cobra-cli add <command> 快速生成命令文件
  • 配合 Viper 实现配置文件 + 环境变量 + 命令行参数三级覆盖
  • 为每个命令写好 ShortLong 描述,--help 会自动生成

本站内容由 褚成志 整理编写,仅供学习参考