Go 编程:写一个程序友好的命令行工具包

作为一名后端程序员,命令行程序太普遍了。如何开始一个类似的命令行程序的工具以及相关依赖包也非常多。个人最常使用的工具包是: spf13/cobra。 同时结合作者提供的spf13/viper包,命令行程序需要的绝大部分功能作者都替你想到了,基本上是没有必要重写一个新轮子了。

然而,即便是功能再齐全的包在个人使用的习惯与便利上还是因人而异的。虽然,通过cobra提供的命令行工具可以快速的启动一个命令行项目的框架代码,但是在多级命令的组织上,还可以在简洁一点,特别是对于子命令的操作,添加一个子命令必须指定对应的父级命令,逻辑上正确,但是使用上是不便的。

rootCmd.AddCommand(subCmd)
...
subCmd.AddCommand(subSubCmd)

cobra生成的代码中,不难发现类似以上的代码。在多级命令的组织上是非常机械。因为是生成代码,不需要手动编辑代码,所以这种不便很难直接感受到。但它提供的cobra工具是程序员友好的。

但是对于企业级命令行项目而言,不可能通过人工的方式通过cobra工具手动生成相应的命令行框架代码。而是需要直接将命令行代码集成的项目框架中去,所以就命令行相关代码而言,我们需要更加程序友好的工具包。

一个理想的命令行工具包,我认为可以是这样的:

整个包仅提供一个增加子命令的接口:Add, 通过option的方式,对具体命令行进行配置即可。特别是对于多级命令的设置上,我希望可以通过类似文件系统的目录结构的形式来定义具体的子级命令。

/			根命令
/foo		一级命令
/foo/bar  	二级命令
...

在使用上,可以是这样:

仅根命令行

import (
	"fmt"

	"github.com/x-mod/cmd"
)

func main() {
	cmd.Add(
		cmd.Path("/"),
		cmd.Main(Main),
	)
	cmd.Execute()
}

func Main(c *cmd.Command, args []string) error {
	fmt.Println("my root command running ...")
	return nil
}

多级命令行:

import (
	"fmt"

	"github.com/x-mod/cmd"
)

func main() {
	cmd.Add(
		cmd.Path("/foo/bar/v1"),
		cmd.Main(V1),
	).PersistentFlags().StringP("parameter", "p", "test", "flags usage")
	cmd.Version("version string")
	cmd.Execute()
}

func V1(c *cmd.Command, args []string) error {
	fmt.Println("V1 called")
	return nil
}

在Bash中,命令行操作如下:

$: go run main.go foo bar v1
V1 called

当然,现实中我也是这么做的。具体工具包的实现,请参考: x-mod/cmd。有了这样一个程序友好的命令行工具包,下一步就是写一个项目框架的快速启动工具,将这个包用进去了。