Skip to content
On this page

Go 学习笔记(10):cli 命令行的使用

Go 语言的程序编译之后是一个可执行的二进制文件,这个特性使得 Go 非常适合做命令行工具,因为一套代码只需要在不同系统上面编译,就可以在所有系统上直接运行,无需运行环境的安装。这篇博文记录 Go 比较流行的命令行工具的使用心得。

cli 库的使用

源码地址:https://github.com/urfave/cli

一个多命令的案例

这个示例的工具名为 mycli,它支持两个命令:greetcalculate

首先,需要安装 github.com/urfave/cli/v2 这个库:

bash
go get github.com/urfave/cli/v2

接下来,创建一个名为 main.go 的文件,内容如下:

go
package main

import (
	"fmt"
	"os"
	"strconv"

	"github.com/urfave/cli/v2"
)

func main() {
	// 创建一个新的 CLI 应用
	app := cli.NewApp()

	// 设置应用的信息
	app.Name = "mycli"
	app.Usage = "A simple CLI tool"
	app.Version = "1.0.0"

	// 定义 greet 命令
	greetCommand := &cli.Command{
		Name:    "greet",
		Aliases: []string{"g"},
		Usage:   "Greet someone",
		Action: func(c *cli.Context) error {
			name := c.Args().First()
			if name == "" {
				name = "World"
			}
			fmt.Println("Hello,", name)
			return nil
		},
	}

	// 定义 calculate 命令
	calculateCommand := &cli.Command{
		Name:    "calculate",
		Aliases: []string{"c"},
		Usage:   "Perform calculation",
		Flags: []cli.Flag{
			&cli.IntFlag{
				Name:  "num1",
				Usage: "First number",
			},
			&cli.IntFlag{
				Name:  "num2",
				Usage: "Second number",
			},
		},
		Action: func(c *cli.Context) error {
			num1 := c.Int("num1")
			num2 := c.Int("num2")
			result := num1 + num2
			fmt.Printf("%d + %d = %d\n", num1, num2, result)
			return nil
		},
	}

	// 将命令添加到应用中
	app.Commands = []*cli.Command{greetCommand, calculateCommand}

	// 运行应用
	err := app.Run(os.Args)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

这个示例中,首先创建了一个新的 CLI 应用,设置了应用的名称、用途和版本信息。

然后,定义了两个命令:greetcalculate。每个命令都有一个 NameAliasesUsageActionAction 字段指定了命令被执行时的操作。

对于 greet 命令,这里简单地获取第一个参数作为名字,并输出 "Hello, {name}"。如果没有提供参数,则默认使用 "World"。

对于 calculate 命令,这里定义了两个整数类型的标志(flags):num1num2,分别代表两个要计算的数字。在命令执行时,读取这两个标志的值,进行加法运算,并打印结果。

可以尝试以下命令执行:

bash
# 使用 greet 命令
go run main.go greet
go run main.go greet Alice

# 使用 calculate 命令
go run main.go calculate --num1 10 --num2 5

命令的定义

为什么要定义命令?

因为大部分命令行工具都是可以进行多种操作的,比如对于数据库命令行工具来说,最基本是需要支持增删改查,那么至少应该支持 createputgetdelete 等命令,然后才是每个命令的参数。

cli 里面使用 &cli.Command{} 来定义命令,然后在最后将所有定义的命令添加到主应用中:

go
// 将命令添加到应用中
app.Commands = []*cli.Command{greetCommand, calculateCommand}

当然,如果你的命令行工具做的事情很单一,就不需要去定义命令了,直接去定义参数就行。

标志参数的定义

所谓标志参数就是在某个 flag 后面的参数,比如 --name xiaoming 就是将 xiaoming 这个参数传给 name

标志参数是有类型的,在命令行中获取标志参数的方式是直接通过标志名称来获取,比如获取一个字符串类型的参数:

go
name:= ctx.String("name")

标志参数的位置可以随意放置,只要保证标志后面接参数值即可(参数值也有时候可以省略)。

位置参数的定义

位置参数很好理解,在命令行中按照顺序排列的参数就是位置参数,位置参数可以添加很多,但是用不用取决于代码中的读取。

位置参数是一个接口类型:

go
type Args interface {
	// Get returns the nth argument, or else a blank string
	Get(n int) string
	// First returns the first argument, or else a blank string
	First() string
	// Tail returns the rest of the arguments (not the first one)
	// or else an empty string slice
	Tail() []string
	// Len returns the length of the wrapped slice
	Len() int
	// Present checks if there are any arguments present
	Present() bool
	// Slice returns a copy of the internal slice
	Slice() []string
}

获取第一个参数:

go
name := c.Args().First()

name := c.Args().Get(0)

也可以直接用切片的索引:

go
name := c.Args().Slice()[0]

参考资料

项目及文档:

使用 cli 库的参考项目(可参考项目结构):