flag 命令行参数解析

flag是Go语言标准库中提供的一个命令行参数解析框架,我们开发控制台程序时可以用flag包实现控制台参数解析、子命令解析、帮助信息生成等功能,这篇笔记我们介绍flag包的使用。

flag包基本使用

这里我们直接通过一个简单例子的形式演示flag包的使用。下面例子代码接收nameagemarried共3个控制台参数,其中前2个为必传,第3个参数默认值为false

package main

import (
    "flag"
    "fmt"
)

func main() {
    // 定义命令行参数
    name := flag.String("name", "", "Your name")
    age := flag.Int("age", 0, "Your age")
    married := flag.Bool("married", false, "Married or not")
    // 解析命令行参数
    flag.Parse()
    // 参数校验
    if *name == "" {
        fmt.Println("name is empty")
        return
    }
    if *age == 0 {
        fmt.Println("age is empty")
        return
    }
    // 打印获取的参数
    fmt.Printf("name: %s, age: %d, married: %t\n", *name, *age, *married)
}

flag中我们可以使用flag.String()flag.Int()flag.Bool()等函数定义不同数据类型的命令行参数,这些具名的参数被称为Flag参数(标志参数),这些函数的返回值是读取到的数据的指针,定义完变量后,我们需要调用flag.Parse()函数执行参数解析,此时我们就可以拿到变量的值了。如果某个参数是必传的,我们可以随后对变量的值进行判断,对于必传参数如果变量的值此时仍是默认值(意味着必传参数没有传递)就可以打印报错信息并终止程序。

编译程序后,我们可以用如下形式指定命令行参数,并观察程序的输出结果。

demoflag -name Lucy -age 18 -married

对于字符串和整数参数,我们需要在对应参数名后加上参数值,而布尔类型参数则是指定就意味着被设置为了true。除了-name Lucy这种写法,我们也可以使用--name Lucy-name=Lucy--name=Lucy这些传递参数的写法,它们的效果和-name Lucy是完全一样的。对于布尔类型,我们也可以指定-married=false来指定将参数值设置为false

此外,代码中我们还设置了很多提示信息,例如“Your name”、“Your age”等,这些信息用于flag包输出提示信息,这些信息的打印可以通过-h--help触发,提示信息的输出例子如下。

Usage of /home/ubuntu/demoflag:
  -age int
        Your age
  -married
        Married or not
  -name string
        Your name

在某些情况下,我们也可以直接调用flag.Usage()函数触发这些信息的打印。

自定义帮助信息

有些时候自动生成的帮助信息不符合我们的要求,我们可以将自定义的函数设置为Usage(),这样就可以实现自定义帮助信息。

flag.Usage = func() {
    fmt.Println("Usage of demoflag is very simple, I won't explain it.")
}

读取位置参数

除了Flag参数,其余的参数被称为位置参数,我们可以使用flag.Args()函数获取位置参数数组,下面是一个例子。

package main

import (
    "flag"
    "fmt"
)

func main() {
    // 定义命令行参数
    name := flag.String("name", "", "Your name")
    age := flag.Int("age", 0, "Your age")
    married := flag.Bool("married", false, "Married or not")
    // 解析命令行参数
    flag.Parse()
    // 参数校验
    if *name == "" {
        fmt.Println("name is empty")
        return
    }
    if *age == 0 {
        fmt.Println("age is empty")
        return
    }
    // 打印获取的参数
    fmt.Printf("name: %s, age: %d, married: %t\n", *name, *age, *married)
    // 获取位置参数并打印
    args := flag.Args()
    for _, arg := range args {
        fmt.Println(arg)
    }
}

使用子命令

一些比较复杂的命令行工具通常包含多个子命令,例如go命令行工具就包含go modgo buildgo get等多个子命令,flag包也支持子命令的设置,下面是一个例子。

package main

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

func main() {
    // 获取子命令
    if len(os.Args) < 2 {
        fmt.Println("Invalid subcommand")
        return
    }
    subCmd := os.Args[1]
    // 设置update子命令
    updateCmd := flag.NewFlagSet("update", flag.ExitOnError)
    updateUrl := updateCmd.String("url", "", "URL")
    // 设置add子命令
    addCmd := flag.NewFlagSet("add", flag.ExitOnError)
    addName := addCmd.String("name", "", "Your name")
    addAge := addCmd.Int("age", 0, "Your age")
    addMarried := addCmd.Bool("married", false, "Married or not")
    // 解析命令行参数
    flag.Parse()
    // 根据参数输出不同信息
    switch subCmd {
    case "update":
        fmt.Printf("Updating from %s ...\n", *updateUrl)
    case "add":
        fmt.Printf("name: %s, age: %d, married: %t\n", *addName, *addAge, *addMarried)
    default:
        fmt.Println("Invalid subcommand")
    }
}

代码中我们创建了两个FlagSet子命令,我们使用switch...case语句来判断用户使用哪个子命令并输出不同信息。此外,子命令的FlagSet也可以通过设置Usage()函数的方式来定制帮助信息。

作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。
Copyright © 2017-2024 Gacfox All Rights Reserved.
Build with NextJS | Sitemap