hzDocs

Flag

What is Flags

need rewrite this article with english

Flags

What is Flag, and Option?

Inside cmdr.v2, terms Flag and Option are used confusingly, and constantly. But, these two terms are different in fact.

A Flag, means that an object created via the interfaces like app.Flg()/builder.Flg(), which will be used for interpreting an cmdline option, and there is also a coresponding Option item in Option Store that is mapping to it.

A Option item, means that a key-value pair in Option Store. For instance, “app.debug” => true is a kv pair entry. The hierarchical layout of its yaml representation is,

app:
  debug: true

In the most scenes, we would like use Option to represent a key-value pair identified by keyPath, whatever it is a Flag exactly, or not.

Integrated with Option Store

We knew Option Store is a in-memory config manager for hierarchical key-value pairs dataset identified by dottedPath/keyPath. And, an Option can be associated with a Flag, or not. But there is always an Option binded to a Flag.

For the top level flag --debug, the store entry associated with it is app.cmd.debug, since app.cmd is a hard-coded prefix for command line options.

For those flags of subcmds, such as --cacert under subcmd cert / create, the entry dotted path would be app.cmd.cert.create.cacert.

cmd.Store() vs cmd.Set()

Programmatically, you can code with app.Store()/app.Set().

The cmd.Store() returns a subset of app's whole Store, which have a root key pointed to key path app.cmd.jump.to. That means, cmd.Store().MustBool("full") can read and write the state of a command line flag --full.

The cmd.Set() returns the app's whole Store, which equals to app.Set(),So you need a cmd.Set().MustBool("app.cmd.jump.to.full") to access the state of --full. Another way is,

set := cmd.Set().WithPrefix(cli.DottedPathToCommandOrFlag)
println(set.MustBool("full"))
assert.Equal(set, cmd.Store())
 
// for the subcmd cmd pointed to `jump.to`, you can use:
cs := cmd.Store()
assert.Equal(cs, cmd.Set().WithPrefix(cli.CommandsStoreKey, "jump.to"))
assert.Equal(cs, cmd.Set(cli.CommandsStoreKey, "jump.to"))
assert.Equal(cs, cmd.Set(cli.CommandsStoreKey, "jump", "to"))
assert.Equal(cs, cmdr.Set().WithPrefix(cli.CommandsStoreKey, "jump.to"))
assert.Equal(cs, cmdr.Store().WithPrefix("jump.to"))
assert.Equal(cs, cmdr.Store("jump.to"))
assert.Equal(cs, cmdr.Store("jump", "to"))

The similar call to app.Store() will get a subtree pointed to app.cmd, which stores the values of all of command-line flags. So app.Store().MustBool("jump.to.full") can query the same key above.

For a short and concise calling statement, it's useful by using GetXXX/MustXXX on the object of cmd.Store()/app.Store().

The prefix app.cmd is used for serializing the Option Store, such as YAML, TOML, etc.. The notable point is app will be stripped when you're really serializing it. So a key app.some.path.debug will be stored as some.path.debug in a YAML file:

some:
  path:
    debug: false

As a sample, here is a Store's YAML file content:

# The common prefix `app` will be stripped in serializing to a file.
logging:
  file: /var/log/app/stdout.log
server:
  port: 3000
  host: 0.0.0.0
  tls: {}
  domains: []
# The following entries are visible only in memory.
# We list them for showing the completely Store-tree
# more clearly.
cmd:
  debug: false # 对应于 `--debug`
  verbose: false # 对应于 `--verbose`

keyPath/dottedPath

keyPath/dottedPath/dottedKeyPath is a exclusive term specially for Option Store, which provides an anchor to any entries in the Store by the form prefix.subcmds...flag.

For an intance, a dottedPath app.cmd.cert.create.cacert has app.cmd as a special prefix for command-line flags, and cert.create is subcommands cert and its child create, and cacert is the long title of a flag, which belongs to subcmds cert.create.

The flag cacert can be specified by --cacert from command-line.

Long Flag, Short and Alias

The term Long Flag is required for a command-line flag. It is the unique identifier in cmdr system.

A Long Flag has prefix -- in the command-line. For example, --cacert is a flag of subcmds cert.create:

$ app cert create --cacert "./ca.cer"

The Short Flag can be attached to a command-line flag. It uses prefix - in the command-line. It is a shortcut to a flag.

Under cmdr.v2 system, multiple short flags can be attached with one flag by using call like b.Flg(longTitle, shortTitle...).ExtraShorts(...).

Short flag is constantly a single char in POSIX standard. So we can use -v as the shortcut to --verbose flag.

But in cmdr system (both v1 and v2), a short flag can be a string with any length. As a sample, -ap has short title ap, shortcut to --extra-files, this is legal too.

Golang flags uses a wired form different than POSIX standard, which has no short or long flag, all flags have the single hyphen char as its leading prefix. That is why in a golang app -port will be used for setting the listening port number rather than --port.

cmdr is a POSIX-compliant command-line argument parser. So we recommend using -p and --port to specify port number. But you can still make a golang flag like app by specifying a short flag with title name port, this is legal option.

By using the builder interface, Flg(longTitle, shortTitle, ...) allows more alias titles associated with a flag. The alias titles are equavalent to long flag, with the leading double hyphen chars --.

As a sample, b.Flg("version", "V", "ver") defines a version flag with both long, short and alias titles. So the following command-line inputs are avaliable:

$ app --version
$ app -V
$ app --ver

Totally you can always organize your command and flag system in a freestanding form. Keep it concise. Too much command depthes and too much more flags are not kindly enough to end-user.

Compat short flags

Multiple short flags can be compated into one leading hyphen char -, this is POSIX-compliant. The compat form is equavalent to the split form:

-azro === -a -z -r -o === -a -zr -o

Default Value

You can specify the default value to a flag, with different types.

b.Flg("count", "c").Default(3) defined its default value is int(3).

The primitive value, including integer, float, complex, boolean, or string, time, duration, slice can be specified as the default value of a flag.

So b.Flg("interval", "i").Default(3*time.Second) defined a time duration value 3s. End user can input the new value with Golang literal convension, for a example::

$ app -i 8m

It specifys a duration value with 8 minutes, which overrides the default value when you defined it.

Duplicated inputs

In general, duplicated inputs of a flag causes the old value to be overlapped by the new one.

So app -c 3 -c 4 -c 5 means the final value of -c is 5.

The exception is slice value. The duplicated inputs of a flag with default slice value causes the new value to apppend to the old. And, the delimiter char , is used for splitting the items in one input string.

This means, as for a flag b.Flg("add", "a").Default([]string{"a", "b"}), the input app -a z,y -a x -a u,v,w will get the final value []string{"z","y","x","u","v","w"}.

Input in command-line

Stripping quotes

cmdr support stripping the quote chars for a flag:

  • --host=localhost
  • --hostlocalhost
  • --host localhost
  • "--hostlocalhost"
  • '--hostlocalhost'
  • --host"localhost"
  • --host'localhost'
  • --host "localhost"

and so on.

These quote chars will be removed by shell automatically. If not, cmdr will do it.

Slice value

While a flag is associated with a default slice value, such as []string, cmdr will merge the duplicated inputs as once slice. That means, -f a -f b -f c will be translated to the final value []string{"a", "b", "c"}. Since the comma , character will be treated as delimiter for splitting a string into slice, so -f a,b -f c will get the same final value.

Hit info

cmdr maintains a hit info record, including the flag title string, short or long, untranslated raw value, and hit times. Sometimes you could change the bizlogic with these records.

Default behavior of --verbose

The builtin flag --verbose has these behaviors:

  1. nothing input,

    The hidden flags/commands wouldn't be shown on help screen

  2. once (-v),

    The hidden flags/commands could be shown

  3. triple times (-v -v -v or -vvv)

    The vendor-hidden flags/commands will be shown onto help screen

You can redefined the hit times or other hit info. As a case, you could treat the hit times of --verbose as a level in 0..9, and uses the level for how many tip messages can be visible in logging.

For the builtin flags, such as verbose, quiet, debug, the final state, hit-times (level) are organized into Env manager inside package hedzr/is,

Refer these topics for the details:

Styles of short flag

Short flags can have multiple characters rather than single one

We supports a phrase as a short flag title.

In another words, you can blur out the differences between short and long flag, so that the golang flag style and GNU getopt style could live at a same app together.

Golang flag style: the flags are always leading by single hyphen character: -host abc -port 9999

GNU getopt style: both the leading hyphen - and double hyphen -- are avaliable for short and long flag. A short flag has single character or number as its title, mostly in POSIX. The sample: -d -t --retry 3

cmdr style: beyond getopt style, remote the limit about single char title for the short flag. So -host abc and -host abc --retry 3 are legal.

Mixing in flag title and value

Once no ambiguities, any combinations of short flags are ok, even mixing the flag title and raw value string in together:

Therefore -dr3t would be interpted to --debug --retry 3 --timeout

cmdr can backtrace and regress to the up level to solve the matches in parsing time.

额外的话题

How is this guide?

Edit on GitHub

Last updated on