cmdr
is a POSIX/GNU style, command-line UI (CLI) Go library.
It is a getopt-like parser of command-line options,
be compatible with the getopt_long
command line UI, which is an extension of the syntax recommended
by POSIX.
There are couples of enhancements beyond the standard
library flag
.
There is a full Options Store
(configurations) for your
hierarchy configuration data too.
| News | Features | Examples | Docs: For Developer | Docs: Uses Fluent API | At Playground |
Youtube - 李宗盛2013最新單曲 山丘 官方完整版音檔 / Jonathan Lee - Hill CHT + ENU
To review the image frames, go surfing at #1 (comment)
for non-go-modules user:
import "gopkg.in/hedzr/cmdr.v1"
with go-modules enabled:
import "github.com/hedzr/cmdr"
-
WIP: v1.6.19
- added:
GetKibibytesXXX
,GetKilobytesXXX
. with further info, rungo run ./examples/fluent kb --size --help
.
- added:
-
v1.6.18
- added:
WithOnSwitchCharHit
,WithOnPassThruCharHit
- bug fixed
- reviewing and refactoring
- added:
-
For more information to refer to CHANGELOG
cmdr has rich features:
- builds multi-level command and sub-commands
- builds short, long and alias options with kinds of data types
- defines commands and options via fluent api style
- or defines its with enhanced stdlib
flag
style - full featured
Options Store
for hosted any application configurations- watchable external config file and child directory
conf.d
- watchable option value merging event: while option value modified in external config file and loaded automatically.
- watchable option value modifying event: while option value modified (from config file, or programmatically)
- connectable with external configuration center
- watchable external config file and child directory
-
Unix getopt(3) representation but without its programmatic interface.
- Options with short names (
-h
) - Options with long names (
--help
) - Options with aliases (
--helpme
,--usage
,--info
) - Options with and without arguments (bool v.s. other type)
- Options with optional arguments and default values
- Multiple option groups each containing a set of options
- Supports the compat short options
-aux
==-a -u -x
- Supports namespaces for (nested) option groups
- Options with short names (
-
Automatic help screen generation (Generates and prints well-formatted help message)
-
Supports the Fluent API style
Sample codes
root := cmdr.Root("aa", "1.0.3") // Or // .Copyright("All rights reserved", "sombody@example.com") .Header("aa - test for cmdr - hedzr") rootCmd = root.RootCommand() co := root.NewSubCommand(). Titles("ms", "micro-service"). Description("", ""). Group("") // deprecated since v1.6.9 // co.NewFlag(cmdr.OptFlagTypeUint). // Titles("t", "retry"). // Description("", ""). // Group(""). // DefaultValue(3, "RETRY") co.NewFlagV(3). Titles("t", "retry"). Description("", ""). Group(""). Palceholder("RETRY") cTags := co.NewSubCommand(). Titles("t", "tags"). Description("", ""). Group("")
-
Muiltiple API styles:
- Data Definitions style (Classical style): see also root_cmd.go in demo
- Fluent API style: see also main.go in fluent
- go
flag
-like API style: see also main.go in ffmain
-
Strict Mode
- false: Ignoring unknown command line options (default)
- true: Report error on unknown commands and options if strict mode enabled (optional)
enable strict mode:
- env var
APP_STRICT_MODE=true
- hidden option:
--strict-mode
(ifcmdr.EnableCmdrCommands == true
) - entry in config file:
app: strict-mode: true
- env var
-
Supports for unlimited multi-level sub-commands.
-
Supports
-I/usr/include -I=/usr/include
-I /usr/include
option argument specificationsAutomatically allows those formats (applied to long option too):
-I file
,-Ifile
, and-I=files
-I 'file'
,-I'file'
, and-I='files'
-I "file"
,-I"file"
, and-I="files"
-
Supports for
-D+
,-D-
to enable/disable a bool option. -
Supports for PassThrough by
--
. (Passing remaining command line arguments after -- (optional)) -
Supports for options being specified multiple times, with different values
since v1.5.0:
-
and multiple flags
-vvv
==-v -v -v
, thencmdr.FindFlagRecursive("verbose", nil).GetTriggeredTimes()
should be3
-
for bool, string, int, ... flags, last one will be kept and others abandoned:
-t 1 -t 2 -t3
==-t 3
-
for slice flags, all of its will be merged (NOTE that duplicated entries are as is):
slice flag overlapped
--root A --root B,C,D --root Z,A
==--root A,B,C,D,Z
cmdr.GetStringSliceR("root") will return[]string{"A","B","C","D","Z"}
-
-
Smart suggestions for wrong command and flags
since v1.1.3, using Jaro-Winkler distance instead of soundex.
-
Groupable commands and options/flags.
Sortable group name with
[0-9A-Za-z]+\..+
format, eg:1001.c++
,1100.golang
,1200.java
, …;abcd.c++
,b999.golang
,zzzz.java
, …;
-
Sortable commands and options/flags. Or sorted by alphabetic order.
-
Predefined commands and flags:
- Help:
-h
,-?
,--help
,--info
,--usage
,--helpme
, ... - Version & Build Info:
--version
/--ver
/-V
,--build-info
/-#
- Simulating version at runtime with
—version-sim 1.9.1
- generally,
conf.AppName
andconf.Version
are originally. ~~tree
: list all commands and sub-commands.--config <location>
: specify the location of the root config file.
- Simulating version at runtime with
- Verbose & Debug:
—verbose
/-v
,—debug
/-D
,—quiet
/-q
- Generate Commands:
generate shell
:—bash
/—zsh
(todo)/--auto
generate manual
: man 1 ready.generate doc
: markdown ready.
cmdr
Specials:--no-env-overrides
, and--strict-mode
--no-color
: print the plain text to console without ANSI colors.
- Help:
-
Generators
-
Todo:
manual generator, andmarkdown/docx/pdf generators. -
Man Page generator:
bin/demo generate man
-
Markdown generator:
bin/demo generate [doc|mdk|markdown]
-
Bash and Zsh (not yet, todo) completion.
$ bin/wget-demo generate shell --bash
-
-
Predefined external config file locations:
-
/etc/<appname>/<appname>.yml
andconf.d
sub-directory. -
/usr/local/etc/<appname>/<appname>.yml
andconf.d
sub-directory. -
$HOME/.config/<appname>/<appname>.yml
andconf.d
sub-directory. -
$HOME/.<appname>/<appname>.yml
andconf.d
sub-directory. -
all predefined locations are:
predefinedLocations: []string{ "./ci/etc/%s/%s.yml", // for developer "/etc/%s/%s.yml", // regular location: /etc/$APPNAME/$APPNAME.yml "/usr/local/etc/%s/%s.yml", // regular macOS HomeBrew location "$HOME/.config/%s/%s.yml", // per user: $HOME/.config/$APPNAME/$APPNAME.yml "$HOME/.%s/%s.yml", // ext location per user "$THIS/%s.yml", // executable's directory "%s.yml", // current directory },
-
since v1.5.0, uses
cmdr.WithPredefinedLocations("a","b",...),
-
-
Watch
conf.d
directory:-
cmdr.WithConfigLoadedListener(listener)
AddOnConfigLoadedListener(c)
RemoveOnConfigLoadedListener(c)
SetOnConfigLoadedListener(c, enabled)
-
As a feature, do NOT watch the changes on
<appname>.yml
.- since v1.6.9,
WithWatchMainConfigFileToo(true)
allows the main config file<appname>.yml
to be watched.
- since v1.6.9,
-
on command-line:
$ bin/demo --configci/etc/demo-yy ~~debug $ bin/demo --config=ci/etc/demo-yy/any.yml ~~debug $ bin/demo --config ci/etc/demo-yy/any.yml ~~debug
-
supports muiltiple file formats:
- Yaml
- JSON
- TOML
-
cmdr.Exec(root, cmdr.WithNoLoadConfigFiles(false))
: disable loading external config files.
-
-
Overrides by environment variables.
priority level:
defaultValue -> config-file -> env-var -> command-line opts
-
Option Store
- Unify option value extraction:-
cmdr.Get(key)
,cmdr.GetBool(key)
,cmdr.GetInt(key)
,cmdr.GetString(key)
,cmdr.GetStringSlice(key, defaultValues...)
andcmdr.GetIntSlice(key, defaultValues...)
,cmdr.GetDuration(key)
for Option value extractions.- bool
- int, int64, uint, uint64, float32, float64
$ app -t 1 # float: 1.1, 1e10, hex: 0x9d, oct: 0700, bin: 0b00010010
- string
- string slice, int slice (comma-separated)
$ app -t apple,banana # => []string{"apple", "banana"} $ app -t apple -t banana # => []string{"apple", "banana"}
- time duration (1ns, 1ms, 1s, 1m, 1h, 1d, ...)
$ app -t 1ns -t 1ms -t 1s -t 1m -t 1h -t 1d
todo: float, time, duration, int slice, …, all primitive go types- map
- struct:
cmdr.GetSectionFrom(sectionKeyPath, &holderStruct)
-
cmdr.Set(key, value)
,cmdr.SerNx(key, value)
Set()
set value by key without RxxtPrefix, eg:cmdr.Set("debug", true)
for--debug
.SetNx()
set value by exact key. so:cmdr.SetNx("app.debug", true)
for--debug
.
-
Fast Guide for
Get
,GetP
andGetR
:cmdr.GetP(prefix, key)
,cmdr.GetBoolP(prefix, key)
, ….cmdr.GetR(key)
,cmdr.GetBoolR(key)
, …,cmdr.GetMapR(key)
cmdr.GetRP(prefix, key)
,cmdr.GetBoolRP(prefix, key)
, ….
As a fact,
cmdr.Get("app.server.port")
==cmdr.GetP("app.server", "port")
==cmdr.GetR("server.port")
(if cmdr.RxxtPrefix == ["app"]); so:cmdr.Set("server.port", 7100) assert cmdr.GetR("server.port") == 7100 assert cmdr.Get("app.server.port") == 7100
In most cases, GetXxxR() are recommended.
While extracting string value, the evnvar will be expanded automatically but raw version
GetStringNoExpandXXX()
available since v1.6.7. For example:fmt.Println(cmdr.GetStringNoExpandR("kk")) // = $HOME/Downloads fmt.Println(cmdr.GetStringR("kk")) // = /home/ubuntu/Downloads
-
-
cmdr Options Store
internal
rxxtOptions
-
Walkable
- Customizable
Painter
interface to loop each command and flag. - Walks on all commands with
WalkAllCommands(walker)
.
- Customizable
-
Daemon (Linux Only)
rewrote since v1.6.0
import "github.com/hedzr/cmdr/plugin/daemon" func main() { if err := cmdr.Exec(rootCmd, daemon.WithDaemon(NewDaemon(), nil,nil,nil), ); err != nil { log.Fatal("Error:", err) } } func NewDaemon() daemon.Daemon { return &DaemonImpl{} }
See full codes in demo app, and cmdr-http2.
$ bin/demo server [start|stop|status|restart|install|uninstall]
install
/uninstall
sub-commands could installdemo
app as a systemd service.Just for Linux
-
ExecWith(rootCmd *RootCommand, beforeXrefBuilding_, afterXrefBuilt_ HookXrefFunc) (err error)
AddOnBeforeXrefBuilding(cb)
AddOnAfterXrefBuilt(cb)
-
cmdr.WithXrefBuildingHooks(beforeXrefBuilding, afterXrefBuilding)
-
Debugging options:
-
~~debug
: dump all key value pairs in parsed options store$ bin/demo -? ~~debug $ bin/demo -? ~~debug ~~raw # without envvar expanding $ bin/demo -? ~~debug ~~env # print envvar k-v pairs too $ bin/demo -? ~~debug --more
~~debug
depends on--help
present (or invoking a command which have one ore more children) -
InDebugging()
, isdelve (refer to here) - To use it, add-tags=delve
:go build -tags=delve cli/main.go go run -tags=delve cli/main.go --help
-
-
~~tree
: dump all sub-commands$ bin/demo ~~tree
~~tree
is a special option/flag like a command. -
More Advanced features
-
Launches external editor by
&Flag{BaseOpt:BaseOpt{},ExternalTool:cmdr.ExternalToolEditor}
:just like
git -m
, try this command:$ EDITOR=nano bin/demo -m ~~debug
Default is
vim
. And-m "something"
can skip the launching. -
ToggleGroup
: make a group of flags as a radio-button group. -
Safe password input for end-user:
cmdr.ExternalToolPasswordInput
-
head
-like option: treatapp do sth -1973
asapp do sth -a=1973
, just likehead -1
.Flags: []*cmdr.Flag{ { BaseOpt: cmdr.BaseOpt{ Short: "h", Full: "head", Description: "head -1 like", }, DefaultValue: 0, HeadLike: true, }, },
-
limitation with enumerable values:
Flags: []*cmdr.Flag{ { BaseOpt: cmdr.BaseOpt{ Short: "e", Full: "enum", Description: "enum tests", }, DefaultValue: "", // "apple", ValidArgs: []string{"apple", "banana", "orange"}, }, },
While a non-in-list value found, An error (
*ErrorForCmdr
) will be thrown:cmdr.ShouldIgnoreWrongEnumValue = true if err := cmdr.Exec(rootCmd); err != nil { if e, ok := err(*cmdr.ErrorForCmdr); ok { // e.Ignorable is a copy of [cmdr.ShouldIgnoreWrongEnumValue] if e.Ignorable { logrus.Warning("Non-recognaizable value found: ", e) os.Exit(0) } } logrus.Fatal(err) }
-
cmdr.TrapSignals(fn, signals...)
It is a helper to simplify your infidonite loop before exit program:
Sample codes
Here is sample fragment: ```go func enteringLoop() { waiter := cmdr.TrapSignals(func(s os.Signal) { logrus.Debugf("receive signal '%v' in onTrapped()", s) }) go waiter() } ```
-
-
More...
-
short
simple codes with structured data style. -
demo
normal demo with external config files. -
wget-demo
partial-covered for GNUwget
. -
fluent
demostrates how to define your command-ui with the fluent api style. -
a demo to show you how to migrate from go
flag
smoothly. -
cmdr-http2
http2 server with daemon supports, graceful shutdown -
awesome-tool
awesome-tool
is a cli app that fetch the repo stars and generate a markdown summary, accordingly with most of awesome-xxx list in github (such as awesome-go).
To build and test cmdr
:
$ make help # see all available sub-targets
$ make info # display building environment
$ make build # build binary files for examples
$ make gocov # test
# customizing
$ GOPROXY_CUSTOM=https://goproxy.io make info
$ GOPROXY_CUSTOM=https://goproxy.io make build
$ GOPROXY_CUSTOM=https://goproxy.io make gocov
Expand to source codes
root := cmdr.Root("aa", "1.0.1").Header("aa - test for cmdr - hedzr")
rootCmd = root.RootCommand()
co := root.NewSubCommand().
Titles("ms", "micro-service").
Description("", "").
Group("")
co.NewFlag(cmdr.OptFlagTypeUint).
Titles("t", "retry").
Description("", "").
Group("").
DefaultValue(3, "RETRY")
cTags := co.NewSubCommand().
Titles("t", "tags").
Description("", "").
Group("")
cTags.NewFlag(cmdr.OptFlagTypeString).
Titles("a", "addr").
Description("", "").
Group("").
DefaultValue("consul.ops.local", "ADDR")
cTags.NewSubCommand().
Titles("ls", "list").
Description("", "").
Group("").
Action(func(cmd *cmdr.Command, args []string) (err error) {
return
})
cTags.NewSubCommand().
Titles("a", "add").
Description("", "").
Group("").
Action(func(cmd *cmdr.Command, args []string) (err error) {
return
})
Try its out :
- https://play.golang.org/p/ieExm3V1Pcx
- wget-demo at playground: https://play.golang.org/p/wpEZgQGzKyt
- demo with daemon plugin: https://play.golang.org/p/wJUA59uGu2M
- https://github.com/hedzr/consul-tags
- https://github.com/hedzr/ini-op
- https://github.com/hedzr/awesome-tool
- austr
- Issue me to adding yours
Feel free to issue me bug reports and fixes. Many thanks to all contributors.
MIT.