命令行工具

Cobra 是 Go 命令行应用框架,支持嵌套子命令、POSIX Flag、帮助信息、命令建议和 Shell 自动补全。

go get github.com/spf13/cobra@latest

根命令

每个命令都是一个 cobra.Command。程序入口只负责执行根命令:

var rootCmd = &cobra.Command{
    Use:           "app",
    Short:         "Example command-line application",
    SilenceUsage:  true,
    SilenceErrors: true,
}

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

SilenceUsage 避免业务执行失败时输出整段帮助,SilenceErrors 让入口统一决定错误格式和退出码。

子命令与参数

var userGetCmd = &cobra.Command{
    Use:   "get <id>",
    Short: "Get a user",
    Args:  cobra.ExactArgs(1),
    RunE: func(cmd *cobra.Command, args []string) error {
        user, err := service.GetUser(cmd.Context(), args[0])
        if err != nil {
            return fmt.Errorf("get user: %w", err)
        }

        return json.NewEncoder(cmd.OutOrStdout()).Encode(user)
    },
}

func init() {
    userCmd := &cobra.Command{Use: "user", Short: "Manage users"}
    userCmd.AddCommand(userGetCmd)
    rootCmd.AddCommand(userCmd)
}

执行方式为:

app user get user-1

优先使用返回 errorRunE,不要在命令内部调用 log.Fatalos.Exit,否则难以测试和统一处理错误。

Flag

局部 Flag 只属于当前命令,Persistent Flag 会被所有子命令继承:

var output string
var configFile string

func init() {
    userGetCmd.Flags().StringVarP(&output, "output", "o", "table",
        "output format: table or json")

    rootCmd.PersistentFlags().StringVar(&configFile, "config", "",
        "path to configuration file")
}

必需 Flag 应显式标记,并处理返回的错误:

if err := userGetCmd.MarkFlagRequired("output"); err != nil {
    panic(err)
}

配置较多时可以把 Cobra 的 Flag 绑定到 Viper,但业务代码仍应接收解析后的配置结构体,而不是直接依赖 Cobra 或 Viper。

Shell 补全

Cobra 可以生成 Bash、Zsh、Fish 和 PowerShell 补全脚本:

completionCmd := &cobra.Command{
    Use:       "completion [bash|zsh|fish|powershell]",
    Args:      cobra.ExactValidArgs(1),
    ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
    RunE: func(cmd *cobra.Command, args []string) error {
        switch args[0] {
        case "bash":
            return rootCmd.GenBashCompletion(cmd.OutOrStdout())
        case "zsh":
            return rootCmd.GenZshCompletion(cmd.OutOrStdout())
        case "fish":
            return rootCmd.GenFishCompletion(cmd.OutOrStdout(), true)
        default:
            return rootCmd.GenPowerShellCompletion(cmd.OutOrStdout())
        }
    },
}

测试

使用可替换的输入输出和 Context 测试命令,不需要启动子进程:

buffer := new(bytes.Buffer)
rootCmd.SetOut(buffer)
rootCmd.SetErr(buffer)
rootCmd.SetArgs([]string{"user", "get", "user-1", "--output", "json"})

err := rootCmd.ExecuteContext(context.Background())

测试之间不要复用已经执行过且包含可变全局 Flag 的命令树。较大的 CLI 可以用构造函数创建新的根命令,并把 Service 作为参数注入。

代码生成器

官方 cobra-cli 可以初始化项目并生成命令文件:

go install github.com/spf13/cobra-cli@latest
cobra-cli init
cobra-cli add user

生成器只是脚手架,并不是运行 Cobra 应用的必要依赖。项目结构稳定后,也可以直接手写 cobra.Command