(二)命令行选项

(二)命令行选项

前言

在第一部分,我们实现了一个“简单的命令行程序”-list,用于显示当前目录下的所有文件,文件名之间使用空格分隔。

今天我们一起来增强下 “list” 命令行,给它传递选项。

传递选项的目的

传递选项是为了改变命令行程序的行为。我们之前实现的 list 命令行程序的默认行为是:显示当前目录下的所有文件,文件名称之间使用空格分隔。

现在我们希望通过传递 -l 选项告诉 list 命令行在显示文件名称的时候,每一行只显示一个文件名(即使用换行符分隔)而不是使用空格分隔。

因此我们的目标是:当我们在终端输入以下命令:

list -l

输出当前目录下所有文件的名称,且每行只显示一个文件名。

命令行选项说明

严格来说,命令行选项是键值对,看个例子:

nginx -s reload

我们给 nginx 命令传递了一个选项 ‘-s’,选项的值为 reload。再看一个例子:

ls -l

这里我们给 ls 命令传递一个选项 ‘-l’,但是你会发现这里只有选项,却没有选项的值。一般我们称这种没有值的选项为”开关选项“。可以认为选项的值是 true。当省略选项本身的时候,则相当于:

ls -l false
# 但是我们一般不会这样写,而是直接省略选项
ls

实现方法

回到我们今天的目标本身,实现list -l

这里有两个关键问题:

  • 如何定义选项
  • 如何获取选项的值

在上一篇中,我们实现了列出当前文件夹下的文件名称,并使用空格分隔文件名,主要代码如下:

var rootCmd = &cobra.Command{
	Use:   "list",
	Short: "列出当前文件下的内容",
	Long: `列出当前文件下所有的文件名称,包括文件名和目录名。`,
	Run: func(cmd *cobra.Command, args []string) {
		// 这里写命令行逻辑
		listDirContent()
	},
}

func listDirContent() {
    // 获取当前工作目录
    wd, _ := os.Getwd()
    // 读取目录下的文件
    files, _ := ioutil.ReadDir(wd)
    fileNames := make([]string, 0, len(files))
    for _, file := range files {
        fileNames = append(fileNames, file.Name())
    }
    fmt.Println(strings.Join(fileNames, " "))
}

如果传递了 “-l” 选项,则使用换行符分隔文件名。使用换行符分隔文件名很简单,我们只需要修改下 listDirContent 方法,给他添加一个参数:分隔符。

func listDirContent(sep string) {
    // ... 内容和上面的相同,这里省略。
    fmt.Println(strings.Join(fileNames, sep))
}

让我们回到 root.go 中看看 init 方法

func init() {
	cobra.OnInitialize(initConfig)
	
	// 定义了config选项
	rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.xtools-guide.yaml)")
	// 定义了toggle选项
	rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

cobra-init 生成的命令,已经包含了两个选项:一个是 config,一个是 toggle。我们再定义一个 “-l” 选项。

rootCmd.Flags().BoolP("lst", "l", false, "列出文件名,并使用换行符分隔")

可以看到,选项的定义是通过 Flags() 下的各种变体方法完成的。上面的 BoolP 就是用来定义开关型选项的方法。第一个参数是选项的“长名称”,即 lst;第二个参数是选项的“短名称”,即 l;第三个参数是选项的默认值,最后一个参数是选项的帮助信息。

在执行命令的时候,我们可以使用选项“长名称”,也可以使用“短名称”,就拿上面的例子来说,下面两种运行方式是等价的:

list --lst
list -l

注意:长名称前有两个横线,短名称只需要一个横线。长名称更便于理解选项的含义,但是短名称书写更简洁。

一行代码我们就完成了选项的定义,接下来我们要获取选项的值。并根据选项的值决定使用换行还是空格分隔文件名。获取选项的值也很简单:

var rootCmd = &cobra.Command{
	Use:   "list",
	Short: "列出当前文件下的内容",
	Long: `列出当前文件下所有的文件名称,包括文件名和目录名。`,
	Run: func(cmd *cobra.Command, args []string) {
		// 注意:需要通过长名称获取选项的值
		value, _ := cmd.Flags().GetBool("lst")
		sep := " "
		if value {
			sep = "\n"
		}
		listDirContent(sep)
	},
}

func listDirContent(sep) {
	// ...
}

可以看到获取选项的值是通过 Flags().GetXX()方法获取,由于我们定义的选项是开关类型,所有通过 GetBool 获取。如果选项是其它类型,还有 GetString、GetInt 等获取选项值的方法。

完整代码放在 github 上,戳这里查看。或者 clone 整个 list 项目,切换到 easy-command-part2 分支查看。

牛刀小试

go run main.go -l
# 或者
go build
./list -l

如果没有意外,应该能看到命令行输入了当前目录下的所有文件名,且每行一个文件名。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值