# Flags

# 什么是标志(Flag)、选项(Option)

cmdr 中,标志和选项常常是混用的。但它们对于 cmdr 来说,确实有分别。

一个标志(Flag),指的是通过 cmdr.NewBool() 等接口函数定义的实体,这个实体被用于命令行参数的解释,对应着一个特定的命令行参数选项。

一个选项(Option),指的是储存在 Option Store 中的一个条目,例如 “app.debug” => true。它的 yaml 表示能够体现出层级关系:

app:
  debug: true
1
2

#Option Store 相集成

一个选项可能和一个标志相关联,但也可能不。但一个标志一定会对应着一个选项。

对于 app.debug 这个选项来说,由于 app 是内部设定的选项前缀(所以在体现到标志时不予考虑),故顶级标志 --debug 与其是关联的。

而对于多级子命令的标志,例如 子命令 cert / create 的标志 --cacert,会对应着 app.cert.create.cacert 这个选项。

当使用 GetXXX 来获取选项值时,R版本能够忽略 app 前缀的指定,从而简化你的编程思路。也就是说,GetBoolR("debug") 将会取得 app.debug 的布尔量值。

app 前缀是为了在序列化 Option Store 为 YAML 或者其他外部格式时而特别建立的前缀,这样能够保证 Option Store 的序列化内容能够被恰当地包含到外部配置中心里(无论是 YAML,JSON,TOML,抑或是显式的微服务外部配置中心)。

# keyPath

keyPathOption Store 中的专属概念,一个选项能够被通过 前缀.子命令序列.标志长格式 的方式被访问,这个格式就被称作 keyPath

例如 app.cert.create.cacert 中,app 是内置的前缀,cert.create 代表着 cert 命令及其 create 子命令,cacert 除了是标志的长格式字串之外,也表达出了从属于 cert.create 子命令的含义。

# Define a flag

增加一个标志(Flag)定义非常容易。

# Bool

	cmdr.NewBool().
		Titles("version", "V").
		Description("display the version of Wget and exit").
		Group(cStartup).
		Action(func(cmd *cmdr.Command, args []string) (err error) {
			cmd.PrintVersion()
			return cmdr.ErrShouldBeStopException
		}).
		AttachTo(root)
1
2
3
4
5
6
7
8
9

在标志的 Action 函数体中,retun cmdr.ErrShouldBeStopException 能够立即终止 cmdr的处理循环并退出应用程序,剩余的命令行参数将被放弃。

通过 NewBool完成了定义的最后,你一定要使用 AttachTo(parent) 的方式将这个标志挂接给某个命令或子命令。

# 默认值

NewBool(defaultValue...) 要求一个(甚至是多个)可选的默认值作为其参数。

但一般情况下,我们定义一个布尔量的标志总是带有 false 默认值,因此命令行中的 --falg 才能翻转其值为 true。例外的情况有两个:

  1. 按照 POSIX 兼容来说,在命令行中针对短标志可以采用 -f+-f- 的格式来强制其值为 true 和 false。这样的话,我们可以输入这样的命令行:-f+ -f- -f -f+ -f-
  2. 对于 Toggleable Flags Group 的情况,一组可以像 Radio Button 一样翻转的标志组合中,通常总是有一个标志带有默认的 true 值。

# Titles(full, short, aliases...)

full 指定了标志的长格式文字表示。

full 不可以缺省,它同时被用作 keyPath 的一部分(参见 Option Store 的 keyPath)。

由于 Golang 的限制,short 参数无法被省去,但你可以指定 "" 空串给它。

# Description(desc, longDesc...)

长格式的描述文字块是可选参数,如果提供的话,在标志的专属帮助屏中会被显示出来。

# 短标志的组合

对于布尔量的短标志来说,POSIX 要求它们能够被组合到i一起。其含义是:

-azro 等价于 -a -z -r -o,或者等价于 -a -zr -o
1
# cmdr 的增强
  1. cmdr 的短标志不会被限定在单个字母。

    我们支持多个字母数字符号作为短标志。

    换句话说,你可以令长短标志的界限模糊,从而提供同时兼容于 golang flag 风格和 getopt 风格的标志集合。

    Golang flag 风格:命令行参数总是单短横线引导的,如:-host abc -port 9999

    getopt 风格:允许单、双短横线引导,分别代表短、长标志,通常短标志使用单个字母或数字,如:-d -t --retry 3

    cmdr 风格:在完全兼容 getopt 风格的基础上,通过解除单字母短标志限制,使得 cmdr cli app 也能支持 -host abc 这样的 golang flag 风格,甚至是 -host abc --retry 3 这样的混合风格(wget 采用这样的混合性风格)

  2. 在无歧义的情况下,cmdr甚至支持你组合任何短标志。

    所以,-dr3t 能够被解释为 --debug --retry 3 --timeout

    cmdr 采取特别的反向回溯算法来解决这个场景下的多级子命令的多级标志的智能匹配。

# String

	cmdr.NewString("localhost").
    Titles("host", "host").Description("some desc")
    Placeholder("HOST").
		Group(cStartup).
		AttachTo(root)
1
2
3
4
5

这个标志在帮助屏中的输出为:

  -host,  --host=HOST           some desc
1

# 占位符(Placeholder)

占位符 HOST 被应用到长标题的表达形式上。

在命令行中输入时,以下的格式都是有效的:

--host=localhost
--host localhost
--hostlocalhost
1
2
3

这些格式中,单引号或双引号包围具体值均为有效形式,如 --host'localhost'

# 自动化的数值拆箱

cmdr.GetXXX 函数将会从 Option Store 中抽出一个选项/标志值,按照你期待的数据类型,必要时 GetXXX 会自动进行拆箱处理。例如当你抽取值 1,2,3,4 时,GetString会取得一个逗号分隔的列表,而 GetIntSlice 会取得 []int{1,2,3,4}

# Int, Int64, Uint, Uint64

	cmdr.NewInt(2).
    Titles("retry", "r", "retry-times").Description("retry times")
    Placeholder("COUNT").
		Group(cStartup).
		AttachTo(root)
1
2
3
4
5

# Float32, Float64

	cmdr.NewFloat32(3.14).
    Titles("pi", "pi").Description("PI")
		Group(cStartup).
		AttachTo(root)
1
2
3
4

# Complex64, Complex128

  cmdr.NewComplex64(3.14+5i).
    Titles("pi", "pi").Description("PI")
	  Group(cStartup).
	  AttachTo(root)
1
2
3
4

# Duration

  cmdr.NewDuration(3*time.Second).
    Titles("period", "p").Description("period")
	  Group(cStartup).
	  AttachTo(root)
1
2
3
4

在命令行中输入时,可以使用 3s8m20s 等这样的语法,它们将会翻译为等价的 time.Duration 值。

# String Slice

  cmdr.NewStringSlice("s1", "s2", "s3").
    Titles("add", "a").Description("add classes")
	  Group(cStartup).
	  AttachTo(root)
1
2
3
4

对于所有 Slice 数组数据类型来说,命令行输入允许两种形式:

  1. 多次输入:-a s1 -a s2 -a s3
  2. 逗号分隔:-a s1,s2,s3

同时也允许上述两种形式任意组合。

# Int Slice, Uint Slilce

  cmdr.NewIntSlice(1, 2, 3).
    Titles("add", "a").Description("add classes")
	  Group(cStartup).
	  AttachTo(root)
1
2
3
4

# Values From

(TODO)

# from the Environment

# from config files

# from Config Center

# More

# DefaultValue

# Deprecated

# Description

# Examples

# ExternalTool

# HeadLike

# Hidden

# PlaceHolder

# ToggleGroup

# ValidArgs

# Option Store 配置数据

Option Store 中的配置数据来自这些地方:

  • 通过 cmdr.NewBool, cmdr.NewString 等接口定义的命令行标志信息中提供的缺省值。例如:

    	cmdr.NewBool(false).
    		Titles("enable-ueh", "ueh").
    		EnvKeys("ENABLE_UEH").
    		Description("Enables the unhandled exception handler?").
    		AttachTo(root)
    
    1
    2
    3
    4
    5

    这里定义了一个 Bool 类型的标志(Flag),其默认值为 false,如果终端用户没有作出指定,则 Option Store 中会包含该条目且具有 bool 值 false。你可以通过 cmdr.GetBoolR('enable-ueh') 取得该值。

    标志总是带有默认值 false,一般较少会使用 true值。

    Toggleable Flags Group 例外,通常在组里会有一个 Flag 具有默认值 true。

  • 通过命令行参数指定的。例如:

    $ go run ./cli --enable-ueh
    
    1

    这里会通过命令行将 enable-ueh 的值设置为 true。

    此时,通过 cmdr.GetBoolR('enable-ueh') 取得的值将会为 true。

  • 通过环境变量指定的。例如:

    ENABLE_UEH=1 go run /cli
    
    1

    如果在定义 Flag 是没有通过 .EnvKeys() 指定环境变量名,cmdr 会试图查找自动化命名的环境变量名。例如此例中,自动化的环境变量名应该是 APP_ENABLE_UEH,请参考 cmdr.WithEnvPrefix() 的相关说明。

  • 通过配置文件装入的。例如在主配置文件中包含有如下条目:

    app: # 这是配置文件的前缀,可以通过 cmdr.WithOptionsPrefix() 自定义
      simple: # 这是通过 cmdr.Root(appName, version) 所指定的应用程序名
        enable-ueh: true   # 字段名称应该等于 Flag 的 Full 字段值
    
    
    1
    2
    3
    4
  • 通过 cmdr.Set(keyPath, value) 设置的。

🔚