上一节中的flag标识是命令行参数中的一种。本章将通过构造嵌套命令来扩展命令行参数。

和上一节类似,我们同样需要一个main函数来调用并执行。有很多的第三方库支持处理复杂的嵌套参数和标识,在这里我们将研究如何仅使用标准库来实现。

实践

1.建立cmdargs.go:

package main

import (
    "flag"
    "fmt"
    "os"
)

const version = "1.0.0"
const usage = `Usage:

%s [command]

Commands:
    Greet
    Version
`

const greetUsage = `Usage:

%s greet name [flag]

Positional Arguments:
    name
        the name to greet

Flags:
`

// MenuConf 保存嵌套命令行的级别参数
type MenuConf struct {
    Goodbye bool
}

// SetupMenu 初始化flag标识
func (m *MenuConf) SetupMenu() *flag.FlagSet {
    menu := flag.NewFlagSet("menu", flag.ExitOnError)
    menu.Usage = func() {
        fmt.Printf(usage, os.Args[0])
        menu.PrintDefaults()
    }
    return menu
}

// GetSubMenu 返回子菜单的flag集
func (m *MenuConf) GetSubMenu() *flag.FlagSet {
    submenu := flag.NewFlagSet("submenu", flag.ExitOnError)
    submenu.BoolVar(&m.Goodbye, "goodbye", false, "Say goodbye instead of hello")

    submenu.Usage = func() {
        fmt.Printf(greetUsage, os.Args[0])
        submenu.PrintDefaults()
    }
    return submenu
}

// Greet 由greet命令调用
func (m *MenuConf) Greet(name string) {
    if m.Goodbye {
        fmt.Println("Goodbye " + name + "!")
    } else {
        fmt.Println("Hello " + name + "!")
    }
}

// Version 打印存储为const的当前版本值
func (m *MenuConf) Version() {
    fmt.Println("Version: " + version)
}

2.建立main.go:

package main

import (
    "fmt"
    "os"
    "strings"
)


func main() {
    c := MenuConf{}
    menu := c.SetupMenu()

    if err := menu.Parse(os.Args[1:]); err != nil {
        fmt.Printf("Error parsing params %s, error: %v", os.Args[1:], err)
        return
    }

    // 在未输出参数的情况下
    // os.Args[0]是执行文件所在的路径
    // len(os.Args) > 1说明输入了命令行参数
    if len(os.Args) > 1 {

        // 根据分支条件打印输出
        switch strings.ToLower(os.Args[1]) {
        case "version":
            c.Version()
        case "greet":
            f := c.GetSubMenu()
            if len(os.Args) < 3 {
                f.Usage()
                return
            }
            if len(os.Args) > 3 {
                if err := f.Parse(os.Args[3:]); err != nil {
                    fmt.Fprintf(os.Stderr, "Error parsing params %s, error: %v", os.Args[3:], err)
                    return
                }

            }
            c.Greet(os.Args[2])

        default:
            fmt.Println("Invalid command")
            menu.Usage()
            return
        }
    } else {
        menu.Usage()
        return
    }
}

3.命令行输入不同的参数会显示:

$./cmdargs -h
Usage:
./cmdargs [command]
Commands:
Greet
Version
$./cmdargs version
Version: 1.0.0
$./cmdargs greet
Usage:
./cmdargs greet name [flag]
Positional Arguments:
name
the name to greet
Flags:
-goodbye
Say goodbye instead of hello
$./cmdargs greet reader
Hello reader!
$./cmdargs greet reader -goodbye
Goodbye reader!

说明

flag.FlagSets可用于设置预期参数。开发人员需要对各参数进行验证,在命令的正确参数子集中进行解析并定义使用字符串。这很容易出错,需要大量迭代才能保证正确性。

flag包帮助开发者解析命令行参数。本节演示了构建复杂命令行应用的基本方法,包括包级别配置,位置参数,多级命令以及如何根据需要将代码拆分为多个文件。

最后编辑: kuteng  文档更新时间: 2021-01-03 15:03   作者:kuteng