go 命令行选项相关库

1.os.Args

package main  
  
import (  
   "fmt"  
   "os"   "strconv")  
  
func main() {  
   for idx, args := range os.Args {  
      fmt.Println("参数:"+strconv.Itoa(idx)+":", args)  
   }  
  
}

输出:


PS D:\GoWorks\src\Test01> go run .\demo56.go 12 23 34 5
参数:0: C:\Users\Simple\AppData\Local\Temp\go-build2539551104\b001\exe\demo56.exe
参数:1: 12
参数:2: 23
参数:3: 34
参数:4: 5

第一个参数是程序路径本身.

程序中os.Args的类型是 []string ,也就是字符串切片。所以可以在for循环的range中遍历,还可以用 len(os.Args) 来获取其数量。

如果不想要输出os.Args的第一个值,也就是可执行文件本身的信息,可以修改上述程序:

for idx, args := range os.Args[1:] {

将range后面的切片,去掉第一个元素。
输出切片的所有元素,还有更简洁的方式:

init函数:

主要特点

  • 在main函数之前自动执行
  • 不能被调用
  • 没有参数,返回值
  • 可以有多个init函数

2.flag库

这就是用来解析命令行选项的库.
是内置库

基本使用:

package main  
  
import (  
   "flag"  
   "fmt")  
  
var (  
   intflag    int  
   boolflag   bool  
   stringflag string  
)  
  
func init() {  
   flag.IntVar(&intflag, "intflag", 0, "int flag valur")  
   flag.BoolVar(&boolflag, "boolflag", false, "bool flag value")  
   flag.StringVar(&stringflag, "stringflag", "default", "string flag value")  
  
}  
  
func main() {  
   flag.Parse()  
  
   fmt.Println("int flag:", intflag)  
   fmt.Println("bool flag:", boolflag)  
   fmt.Println("string flag:", stringflag)  
  
}
$ go build -o main.exe main.go
$ ./main.exe -intflag 12 -boolflag 1 -stringflag test

输出

int flag: 12
bool flag: true
string flag: test
flag.IntVar(&intflag, "intflag", 0, "int flag valur")  
   flag.BoolVar(&boolflag, "boolflag", false, "bool flag value")  
   flag.StringVar(&stringflag, "stringflag", "default", "string flag value")  

函数中有四个参数
存储的变量的指针地址,命令行选项名,默认值,-h描述值

./main.exe -h
-boolflag
        bool flag value
  -intflag int
        int flag valur
  -stringflag string
        string flag value (default "default")

使用步骤

  • 定义全局变量存储
  • 定义init方法,使用flag.TypeVar定义选项.这里的Type可以为基本类型Int/Uint/Float64/Bool,还可以是时间间隔time.Duration。定义时传入变量的地址、选项名、默认值和帮助信息;
  • 在main中调用flag.Parse从os.Args[1:]中解析选项.

注意:
flag.Parse方法必须在所有选项都定义之后才能调用,在其之后也不能在定义选项.

选项格式:

-flag
-flag=x
-flag x

---都可以使用,它们的作用是一样的。有些库使用-表示短选项,--表示长选项

另一种定义选项的方式

除了使用flag.TypeVar定义,还可以使用flag.Type来定义(其中Type可以为Int/Uint/Bool/Float64/String/Duration等). 这个会自动为我们分配变量,并返回该变量的地址给我们

package main  
  
import (  
   "flag"  
   "fmt")  
  
var (  
   intv    *int  
   boolv   *bool  
   stringv *string  
)  
  
func init() {  
   intv = flag.Int("intv", 0, "int value")  
   boolv = flag.Bool("boolv", false, "bool value")  
   stringv = flag.String("stringv", "default ", "string value")  
}  
func main() {  
   flag.Parse()  
  
   fmt.Println("int flag:", *intv)  
   fmt.Println("bool flag:", *boolv)  
   fmt.Println("string flag:", *stringv)  
  
}

-h

-boolv
        bool value
  -intv int
        int value
  -stringv string
        string value (default "default ")

3.go-flags库

是第三方库
go-flags提供了比标准库flag更多的选项。它利用结构标签(struct tag)和反射提供了一个方便、简洁的接口。

$ go get github.com/jessevdk/go-flags

基本使用

package main  
  
import (  
   "fmt"  
   "github.com/jessevdk/go-flags")  
  
type Option struct {  
   Verbose []bool `short:"v" long:"verbose" description:"Show verbose debug message"`  
}  
  
func main() {  
   var opt Option  
   flags.Parse(&opt)  
   fmt.Println(opt.Verbose)  
}

一般使用步骤:

  • 定义选项结构体,在结构体标签中设置选项信息. 通过short和long来设置长短标签. description设置帮助信息. 命令行传参的时候就支持长和短选项了
  • 声明选项的变量
  • 调用go-flags的解析方法解析.
    短选项:
$ ./main.exe -v
[true]

长选项:

$ ./main.exe --verbose
[true]

由于Verbose字段是切片类型,每次遇到-v--verbose都会追加一个true到切片中。

多个短选项:

$ ./main.exe -v -v
[true true]

多个长选项:

$ ./main.exe --verbose --verbose
[true true]

短选项 + 长选项:

$ ./main.exe -v --verbose -v
[true true true]

短选项合写:

$ ./main.exe -vvv
[true true true]

基本的特性

go-flags比flag支持更多的数据类型:
最明显的就是他还支持切片和map函数.
切片类型选项的不同之处在于,遇到相同的选项时,值会被追加到切片中。而非切片类型的选项,后出现的值会覆盖先出现的值。

package main  
  
import (  
   "fmt"  
   "github.com/jessevdk/go-flags")  
  
type Option struct {  
   IntFlag        int            `short:"i" long:"int" description:"int flag value"`  
   IntSlice       []int          `long:"intslice" description:"int slice flag value"`  
   BoolFlag       bool           `long:"bool" description:"bool flag value"`  
   BoolSlice      []bool         `long:"boolslice" description:"bool slice flag value"`  
   FloatFlag      float64        `long:"float", description:"float64 flag value"`  
   FloatSlice     []float64      `long:"floatslice" description:"float64 slice flag value"`  
   StringFlag     string         `short:"s" long:"string" description:"string flag value"`  
   StringSlice    []string       `long:"strslice" description:"string slice flag value"`  
   PtrStringSlice []*string      `long:"pstrslice" description:"slice of pointer of string flag value"`  
   Call           func(string)   `long:"call" description:"callback"`  
   IntMap         map[string]int `long:"intmap" description:"A map from string to int"`  
}  
  
func main() {  
   var opt Option  
   opt.Call = func(value string) {  
      fmt.Println("in callback", value)  
   }  
   _, err := flags.Parse(&opt)  
   if err != nil {  
      println("parse error", err)  
      return  
   }  
  
   fmt.Printf("int flag: %v\n", opt.IntFlag)  
   fmt.Printf("int slice flag: %v\n", opt.IntSlice)  
   fmt.Printf("bool flag: %v\n", opt.BoolFlag)  
   fmt.Printf("bool slice flag: %v\n", opt.BoolSlice)  
   fmt.Printf("float flag: %v\n", opt.FloatFlag)  
   fmt.Printf("float slice flag: %v\n", opt.FloatSlice)  
   fmt.Printf("string flag: %v\n", opt.StringFlag)  
   fmt.Printf("string slice flag: %v\n", opt.StringSlice)  
   fmt.Println("slice of pointer of string flag: ")  
   for i := 0; i < len(opt.PtrStringSlice); i++ {  
      fmt.Printf("\t%d: %v\n", i, *opt.PtrStringSlice[i])  
   }  
   fmt.Printf("int map: %v\n", opt.IntMap)  
}

注意这个基本类型指针的切片,即上面的PtrStringSlice字段,类型为[]*string, 因为存储的是字符串指针,go-flags在解析的过程中遇到该选项会自动创建字符串, 将指针追加到切片中去.
也就是说,他会创建一个字符串来存储值,并把指向字符串的指针放在指针切片中.

传入--pstrslice选项:

$ ./main.exe --pstrslice test1 --pstrslice test2
slice of pointer of string flag:
    0: test1
    1: test2

另外,我们可以在选项中定义函数类型。该函数的唯一要求是有一个字符串类型的参数。解析中每次遇到该选项就会以选项值为参数调用这个函数。 上面代码中,Call函数只是简单的打印传入的选项值。运行代码,传入--call选项:

$ ./main.exe --call test1 --call test2
in callback:  test1
in callback:  test2

最后,go-flags还支持 map 类型。虽然限制键必须是string类型,值必须是基本类型,也能实现比较灵活的配置。 map类型的选项值中键-值通过:分隔,如key:value,可设置多个。运行代码,传入--intmap选项:

$ ./main.exe --intmap key1:12 --intmap key2:58
int map: map[key1:12 key2:58]

基本设置:

short:            the short name of the option (single character)
					短选项
long:             the long name of the option
					长选项
required:         if non empty, makes the option required to appear on the command
                  line. If a required option is not present, the parser will
                  return ErrRequired (optional)
                  如果非空,则在命令行上显示所需的选项。如果必需的选项没有出现,解析器将返回ErrRequired(可选)
description:      the description of the option (optional)
					选项描述
long-description: the long description of the option. Currently only
                  displayed in generated man pages (optional)
                  对选项的长描述。目前只显示在生成的手册页中(可选)
no-flag:          if non-empty, this field is ignored as an option (optional)如果非空,该字段将作为选项被忽略(可选)

optional:       if non-empty, makes the argument of the option optional. When an
                argument is optional it can only be specified using
                --option=argument (optional)
                如果非空,则使选项的参数为可选。当一个参数是可选的,它只能使用——option=argument (optional)来指定。
                
optional-value: the value of an optional option when the option occurs
                without an argument. This tag can be specified multiple
                times in the case of maps or slices (optional)
                当选项不带参数时,可选选项的值。在映射或切片的情况下,此标记可以指定多次(可选)
default:        the default value of an option. This tag can be specified
                multiple times in the case of slices or maps (optional)
                选项的默认值。在切片或映射的情况下,此标记可以指定多次(可选)
default-mask:   when specified, this value will be displayed in the help
                instead of the actual default value. This is useful
                mostly for hiding otherwise sensitive information from
                showing up in the help. If default-mask takes the special
                value "-", then no default value will be shown at all
                (optional)
                指定后,此值将显示在帮助中,而不是实际的默认值。这对于隐藏其他敏感信息而不显示在帮助中非常有用。如果default-mask采用特殊值"-",则根本不会显示默认值(可选)
env:            the default value of the option is overridden from the
                specified environment variable, if one has been defined.
                (optional)
                如果已经定义了环境变量,则该选项的默认值将从指定的环境变量中重写。(可选)
env-delim:      the 'env' default value from environment is split into
                multiple values with the given delimiter string, use with
                slices and maps (optional)
                来自environment的'env'默认值被分割成多个值,使用给定的分隔符字符串,用于切片和映射(可选)
value-name:     the name of the argument value (to be shown in the help)
                (optional)
                参数值的名称(将在帮助中显示)(可选)
choice:         limits the values for an option to a set of values.
                Repeat this tag once for each allowable value.
                e.g. `long:"animal" choice:"cat" choice:"dog"`
                将一个选项的值限制为一组值。对于每个允许的值重复此标记一次。如。长:“动物”选项:“猫”选项:“狗”
hidden:         if non-empty, the option is not visible in the help or man page.
				如果非空,则该选项在帮助或手册页中不可见。

base: a base (radix) used to convert strings to integer values, the
      default base is 10 (i.e. decimal) (optional)
      用于将字符串转换为整数值的基数(基数),默认基数为10(即十进制)(可选)

ini-name:       the explicit ini option name (optional)
				显式ini选项名称(可选)
no-ini:         if non-empty this field is ignored as an ini option
                (optional)
                如果非空,该字段将被忽略为ini选项(可选)

group:                when specified on a struct field, makes the struct
                      field a separate group with the given name (optional)
                      在结构字段上指定时,使结构字段成为具有给定名称的单独组(可选)
namespace:            when specified on a group struct field, the namespace
                      gets prepended to every option's long name and
                      subgroup's namespace of this group, separated by
                      the parser's namespace delimiter (optional)
                      当在组结构字段上指定名称空间时,该名称空间被前置到每个选项的长名称和该组的子组的名称空间之前,由解析器的名称空间分隔符分隔(可选)
env-namespace:        when specified on a group struct field, the env-namespace
                      gets prepended to every option's env key and
                      subgroup's env-namespace of this group, separated by
                      the parser's env-namespace delimiter (optional)
                      当在组结构字段上指定env-namespace时,env-namespace将被前置到每个选项的env键和该组的子组的env-namespace之前,由解析器的env-namespace分隔符分隔(可选)
command:              when specified on a struct field, makes the struct
                      field a (sub)command with the given name (optional)
                      当在结构字段上指定时,使结构字段成为具有给定名称的(sub)命令(可选)
subcommands-optional: when specified on a command struct field, makes
                      any subcommands of that command optional (optional)
                      当在一个命令结构字段上指定时,使得该命令的任何子命令成为可选的(可选)
alias:                when specified on a command struct field, adds the
                      specified name as an alias for the command. Can be
                      be specified multiple times to add more than one
                      alias (optional)
                      在命令结构字段上指定时,将指定的名称添加为命令的别名。可以多次指定以添加多个别名(可选)
positional-args:      when specified on a field with a struct type,
                      uses the fields of that struct to parse remaining
                      positional command line arguments into (in order
                      of the fields). If a field has a slice type,
                      then all remaining arguments will be added to it.
                      Positional arguments are optional by default,
                      unless the "required" tag is specified together
                      with the "positional-args" tag. The "required" tag
                      can also be set on the individual rest argument
                      fields, to require only the first N positional
                      arguments. If the "required" tag is set on the
                      rest arguments slice, then its value determines
                      the minimum amount of rest arguments that needs to
                      be provided (e.g. `required:"2"`) (optional)
                      当在具有结构类型的字段上指定时,使用该结构的字段将其余位置命令行参数解析为(按字段顺序)。如果字段具有切片类型,则所有剩余参数将被添加到该字段中。默认情况下,位置参数是可选的,除非"required"标记与"position -args"标记一起指定。“required”标签也可以设置在单独的其余参数字段上,只需要前N个位置参数。如果在其余参数片上设置了“required”标记,那么它的值决定了最小的保留量
positional-arg-name:  used on a field in a positional argument struct; name
                      of the positional argument placeholder to be shown in
                      the help (optional)
                      用于位置参数结构中的字段;帮助中显示的位置参数占位符的名称(可选)

也就是结构体标签中可以设置的选项就有那么多.
都机器翻译了一下,方便看.

require:

非空时,表示对应的选项必须设置值,否则就会解析返回ErrRequired错误.

default

default :用来设置选项的默认值.
如果设置了默认值,那么require是否设置也就不影响了.

package main

import (
  "fmt"
  "log"

  "github.com/jessevdk/go-flags"
)

type Option struct {
  Required    string  `short:"r" long:"required" required:"true"`
  Default     string  `short:"d" long:"default" default:"default"`
}

func main() {
  var opt Option
  _, err := flags.Parse(&opt)
  if err != nil {
    log.Fatal("Parse error:", err)
  }
    
  fmt.Println("required: ", opt.Required)
  fmt.Println("default: ", opt.Default)
}

group:

group:                when specified on a struct field, makes the struct
                      field a separate group with the given name (optional)
                      在结构字段上指定时,使结构字段成为具有给定名称的单独组(可选)
package main

import (
  "fmt"
  "log"
  "os"
    
  "github.com/jessevdk/go-flags"
)

type Option struct {
  Basic GroupBasicOption `description:"basic type" group:"basic"`
  Slice GroupSliceOption `description:"slice of basic type" group:"slice"`
}

type GroupBasicOption struct {
  IntFlag    int     `short:"i" long:"intflag" description:"int flag"`
  BoolFlag   bool    `short:"b" long:"boolflag" description:"bool flag"`
  FloatFlag  float64 `short:"f" long:"floatflag" description:"float flag"`
  StringFlag string  `short:"s" long:"stringflag" description:"string flag"`
}

type GroupSliceOption struct {
  IntSlice		int			`long:"intslice" description:"int slice"`
  BoolSlice		bool		`long:"boolslice" description:"bool slice"`
  FloatSlice	float64	`long:"floatslice" description:"float slice"`
  StringSlice	string	`long:"stringslice" description:"string slice"`
}

func main() {
  var opt Option
  p := flags.NewParser(&opt, flags.Default)
  _, err := p.ParseArgs(os.Args[1:])
  if err != nil {
    log.Fatal("Parse error:", err)
  }
    
  basicGroup := p.Command.Group.Find("basic")
  for _, option := range basicGroup.Options() {
    fmt.Printf("name:%s value:%v\n", option.LongNameWithNamespace(), option.Value())
  }
	
  sliceGroup := p.Command.Group.Find("slice")
  for _, option := range sliceGroup.Options() {
    fmt.Printf("name:%s value:%v\n", option.LongNameWithNamespace(), option.Value())
  }
}

上面代码中我们将基本类型和它们的切片类型选项拆分到两个结构体中,这样可以使代码看起来更清晰自然,特别是在代码量很大的情况下。 这样做还有一个好处,我们试试用--help运行该程序:

$ ./main.exe --help
Usage:
  D:\code\golang\src\github.com\darjun\go-daily-lib\go-flags\group\main.exe [OPTIONS]

basic:
  /i, /intflag:      int flag
  /b, /boolflag      bool flag
  /f, /floatflag:    float flag
  /s, /stringflag:   string flag

slice:
  /intslice:     int slice
  /boolslice     bool slice
  /floatslice:   float slice
  /stringslice:  string slice

Help Options:
  /?                 Show this help message
  /h, /help          Show this help message

帮助信息也是分组的

子命令

所谓的子命令就是在输入的时候不是-也不是--为标识的选项.

go-flags支持子命令。我们经常使用的 Go 和 Git 命令行程序就有大量的子命令。例如go versiongo buildgo rungit statusgit commit这些命令中version/build/run/status/commit就是子命令。 使用go-flags定义子命令比较简单:

package main  
  
import (  
   "errors"  
   "fmt"   "log"   "strconv"   "strings"  
   "github.com/jessevdk/go-flags")  
  
type MathCommand struct {  
   Op     string `long:"op" description:"operation to execute"`  
   Args   []string  
   Result int64  
}  
  
func (this *MathCommand) Execute(args []string) error {  
   if this.Op != "+" && this.Op != "-" && this.Op != "x" && this.Op != "/" {  
      return errors.New("invalid op")  
   }  
  
   for _, arg := range args {  
      num, err := strconv.ParseInt(arg, 10, 64)  
      if err != nil {  
         return err  
      }  
  
      this.Result += num  
   }  
  
   this.Args = args  
   return nil  
}  
  
type Option struct {  
   Math MathCommand `command:"math"`  
}  
  
func main() {  
   var opt Option  
   _, err := flags.Parse(&opt)  
  
   if err != nil {  
      log.Fatal(err)  
   }  
  
   fmt.Printf("The result of %s is %d", strings.Join(opt.Math.Args, opt.Math.Op), opt.Math.Result)  
}

子命令必须实现go-flags定义的Commander接口:

type Commander interface {
    Execute(args []string) error
}

解析命令行时,如果遇到不是以---开头的参数,go-flags会尝试将其解释为子命令名。子命令的名字通过在结构标签中使用command指定。 子命令后面的参数都将作为子命令的参数,子命令也可以有选项。

上面代码中,我们实现了一个可以计算任意个整数的加、减、乘、除子命令math

使用:

$ ./main.exe math --op + 1 2 3 4 5
The result of 1+2+3+4+5 is 15

$ ./main.exe math --op - 1 2 3 4 5
The result of 1-2-3-4-5 is -13

$ ./main.exe math --op x 1 2 3 4 5
The result of 1x2x3x4x5 is 120

$ ./main.exe math --op ÷ 120 2 3 4 5
The result of 120÷2÷3÷4÷5 is 1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值