## 解析命令行参数 上一节中的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包帮助开发者解析命令行参数。本节演示了构建复杂命令行应用的基本方法,包括包级别配置,位置参数,多级命令以及如何根据需要将代码拆分为多个文件。 * * * * 学识浅薄,错误在所难免。欢迎在群中就本书提出修改意见,以飨后来者,长风拜谢。 Golang中国(211938256) beego实战(258969317) Go实践(386056972)