Fully featured Go (golang) command line option parser with built-in auto-completion support.

Overview

go-getoptions

Quick overview

  1. Define your command line specification:

    package main
    
    import (
    	"fmt"
    	"io/ioutil"
    	"log"
    	"os"
    
    	"github.com/DavidGamba/go-getoptions"
    )
    
    var logger = log.New(ioutil.Discard, "DEBUG: ", log.LstdFlags)
    
    func main() {
    	var debug bool
    	var greetCount int
    	var list map[string]string
    	opt := getoptions.New()
    	opt.Bool("help", false, opt.Alias("h", "?"))
    	opt.BoolVar(&debug, "debug", false)
    	opt.IntVar(&greetCount, "greet", 0,
    		opt.Required(),
    		opt.Description("Number of times to greet."))
    	opt.StringMapVar(&list, "list", 1, 99,
    		opt.Description("Greeting list by language."))
    	remaining, err := opt.Parse(os.Args[1:])
    	if opt.Called("help") {
    		fmt.Fprintf(os.Stderr, opt.Help())
    		os.Exit(1)
    	}
    	if err != nil {
    		fmt.Fprintf(os.Stderr, "ERROR: %s\n\n", err)
    		fmt.Fprintf(os.Stderr, opt.Help(getoptions.HelpSynopsis))
    		os.Exit(1)
    	}
    
    	// Use the passed command line options... Enjoy!
    	if debug {
    		logger.SetOutput(os.Stderr)
    	}
    	logger.Printf("Unhandled CLI args: %v\n", remaining)
    
    	// Use the int variable
    	for i := 0; i < greetCount; i++ {
    		fmt.Println("Hello World, from go-getoptions!")
    	}
    
    	// Use the map[string]string variable
    	if len(list) > 0 {
    		fmt.Printf("Greeting List:\n")
    		for k, v := range list {
    			fmt.Printf("\t%s=%s\n", k, v)
    		}
    	}
    }
  2. Call it:

    Show help
    $ ./myscript --help
    SYNOPSIS:
        myscript --greet <int> [--debug] [--help|-h|-?] [--list <key=value>...]...
    
    REQUIRED PARAMETERS:
        --greet <int>                Number of times to greet.
    
    OPTIONS:
        --debug                      (default: false)
    
        --help|-h|-?                 (default: false)
    
        --list <key=value>...        Greeting list by language. (default: {})
    Show errors
    $ ./myscript
    ERROR: Missing required option 'greet'!
    
    SYNOPSIS:
        myscript --greet <int> [--debug] [--help|-h|-?] [--list <key=value>...]...
    Show errors
    $ ./myscript -g
    ERROR: Missing argument for option 'greet'!
    
    SYNOPSIS:
        myscript --greet <int> [--debug] [--help|-h|-?] [--list <key=value>...]...
    Use of int option
    $ ./myscript -g 3
    Hello World, from go-getoptions!
    Hello World, from go-getoptions!
    Hello World, from go-getoptions!
    Use of bool option
    $ ./myscript --debug -g 1 other stuff
    DEBUG: 2019/07/14 23:20:22 Unhandled CLI args: [other stuff]
    Hello World, from go-getoptions!
    Use of map option
    ./myscript -g 0 -l en='Hello World' es='Hola Mundo'
    Greeting List:
            en=Hello World
            es=Hola Mundo

Examples

Simple script

A simple script ./examples/myscript/main.go

To use the autocompletion, cd to the ./examples/myscript dir and run: source sourceme.bash The run go build and ./myscript.

Tab completion for this script is triggered for options only, so you need to have a dash - to trigger it: ./myscript -<tab><tab>

Program with multiple commands separated in multiple packages

This is the other extreme, a large program that can separate each command in a separate go package.

The base is located at ./examples/mygit/main.go

The commands are located at:

To use the autocompletion, cd to the ./examples/mygit dir and run: source sourceme.bash The run go build and ./mygit.

Tab completion without arguments triggers completion for commands, for option completion add a dash - and trigger it: ./mygit -<tab><tab>

The slow command shows an example of an slow command that can be cancelled with Ctrl+C. The cancellation is passed to the command through context.Context and it is handled at the command to stop taking new work and trigger a cleanup routine. Running Ctrl+C twice cancels the cancellation routine and fully cancels the program.

Directed Acyclic Graph Build System

This example shows task dependency orchestration and parallelization ./examples/dag/main.go.

To use the autocompletion, cd to the ./examples/dag dir and run: source sourceme.bash The run go build and ./dag.

Tab completion without arguments triggers completion for commands, for option completion add a dash - and trigger it: ./dag -<tab><tab>

DAG Build System

For an overview of the Directed Acyclic Graph Build System see ./dag/README.adoc

Features

  • Built in auto completion. A single line of bash is all it takes.

  • Allow passing options and non-options in any order.

  • Support for --long options.

  • Support for short (-s) options with flexible behaviour (see the Operation Modes section for details):

    • Normal (default)

    • Bundling

    • SingleDash

  • Called() method indicates if the option was passed on the command line.

  • Multiple aliases for the same option. e.g. help, man.

  • CalledAs() method indicates what alias was used to call the option on the command line.

  • Simple synopsis and option list automated help.

  • Boolean, String, Int, Float64, Slice and Map type options.

  • Negatable Boolean options.

    For example: --verbose, --no-verbose or --noverbose.

  • Options with Array arguments. The same option can be used multiple times with different arguments. The list of arguments will be saved into an Slice.

  • Options with array arguments and multiple entries.

    For example, instead of writing: color --r 10 --g 20 --b 30 --next-option or color --rgb 10 --rgb 20 --rgb 30 --next-option the input could be: color --rgb 10 20 30 --next-option

  • When using integer array options with multiple arguments, positive integer ranges are allowed.

    For example, Instead of writing: csv --columns 1 2 3 or csv --columns 1 --columns 2 --columns 3 The input could be: csv --columns 1..3

  • Options with Key Value arguments. This allows the same option to be used multiple times with arguments of key value type.

    For example: rpmbuild --define name=myrpm --define version=123

  • Options with key value arguments and multiple entries.

    For example, instead of writing: connection --server hostname=serverIP --server port=123 --client hostname=localhost --client port=456 the input could be: connection --server hostname=serverIP port=123 --client hostname=localhost port=456

  • Supports command line options with '='.

    For example: You can use --string=mystring and --string mystring.

  • Allows passing arguments to options that start with dash - when passed after equal.

    For example: --string=--hello and --int=-123.

  • Supports passing -- to stop parsing arguments (everything after will be left in the remaining []string).

  • Options with optional arguments. If the default argument is not passed the default is set.

    For example: You can call --int 123 which yields 123 or --int which yields the given default.

  • Allows abbreviations when the provided option is not ambiguous.

    For example: An option called build can be called with --b, --bu, --bui, --buil and --build as long as there is no ambiguity. In the case of ambiguity, the shortest non ambiguous combination is required.

  • Support for the lonesome dash "-". To indicate, for example, when to read input from STDIO.

  • Incremental options. Allows the same option to be called multiple times to increment a counter.

  • Supports case sensitive options. For example, you can use v to define verbose and V to define Version.

  • Support indicating if an option is required and allows overriding default error message.

  • Errors exposed as public variables to allow overriding them for internationalization.

  • Supports program commands (when a command is passed a command function is triggered to handle the command logic).

  • Built in opt.Dispatch function calls commands and propagates context, options, arguments and cancellation signals.

  • Multiple ways of managing unknown options:

    • Fail on unknown (default).

    • Warn on unknown.

    • Pass through, allows for commands and can be combined with Require Order.

  • Require order: Allows for commands. Stop parsing arguments when the first non-option is found. When mixed with Pass through, it also stops parsing arguments when the first unmatched option is found.

  • Set options by reading Environment Variables.

How to install it

  1. Get it from github:

    go get github.com/DavidGamba/go-getoptions

  2. Then import it:

    import "github.com/DavidGamba/go-getoptions" // As getoptions

  3. Enjoy!

Dependencies

Go 1.14+

Only the last two versions of Go will be supported.

Introduction

Note
For a Quick overview, jump to that section in the TOC or review the GoDoc Documentation.

Option parsing is the act of taking command line arguments and converting them into meaningful structures within the program.

An option parser should support, at least, the following:

Boolean options

True when passed on the command line. For example:

ls --all

In go-getoptions this is accomplished with:

  • ptr := opt.Bool(name, default_value).

  • opt.BoolVar(&ptr, name, default_value).

  • Additionally, if all you want to know is if the option was passed you can use: opt.Bool(name, default_value) (without capturing its return value) and then check opt.Called(name).

  • Also, you can get the value with v, ok := opt.Value(name).(bool).

Options with String arguments

The option will accept a string argument. For example:

grepp --ignore .txt

Additionally, arguments to options can be passed with the = symbol.

grepp --ignore=.txt

In go-getoptions this is accomplished with:

  • ptr := opt.String(name, default_value).

  • opt.StringVar(&ptr, name, default_value).

The features listed above are enough to create basic programs but an option parser should do better:

Options with Integer arguments

Parse an option string argument into an Integer and provide an user error if the string provided is not an integer. For example:

grepp --contex-lines 3

and:

grepp --context-lines string

Error: 'string' is not a valid integer.

In go-getoptions this is accomplished with:

  • ptr := opt.Int(name, default_value).

  • opt.IntVar(&ptr, name, default_value).

Options with Floating point arguments

Parse an option string argument into a Floating point value and provide an user error if the string provided is not a valid floating point. For example:

program --approximation 3.5

and:

$ program --approximation string

Error: 'string' is not a valid floating point value.

In go-getoptions this is accomplished with:

  • ptr := opt.Float64(name, default_value).

  • opt.Float64Var(&ptr, name, default_value).

The features listed above relieve the programmer from the cumbersome task of converting the option argument into the expected type.

That covers the most basic set of features, but still it is not enough to get past a basic program. The following features will allow for a more complete interface.

Options with array arguments

This allows the same option to be used multiple times with different arguments. The list of arguments will be saved into a Slice inside the program. For example:

list-files --exclude .txt --exclude .html --exclude .pdf

In go-getoptions this is accomplished with:

  • ptr := opt.StringSlice(name, 1, 1).

  • opt.StringSliceVar(&ptr, name, 1, 1).

  • ptr := opt.IntSlice(name, 1, 1).

  • opt.IntSliceVar(&ptr, name, 1, 1).

go-getoptions has only implemented this feature for string and int.

Options with Key Value arguments

This allows the same option to be used multiple times with arguments of key value type. For example:

rpmbuild --define name=myrpm --define version=123

In go-getoptions this is accomplished with:

  • strMap := opt.StringMap(name, 1, 1).

  • opt.StringMapVar(&ptr, name, 1, 1).

go-getoptions has only implemented this feature for string.

The features above are useful when you have a variable amount of arguments, but it becomes cumbersome for the user when the number of entries is always the same. The features described below are meant to handle the cases when each option has a known number of multiple entries.

Options with array arguments and multiple entries

This allows the user to save typing. For example:

Instead of writing: color --r 10 --g 20 --b 30 --next-option or color --rgb 10 --rgb 20 --rgb 30 --next-option

The input could be: color --rgb 10 20 30 --next-option.

The setup for this feature should allow for the user to continue using both versions of the input, that is passing one argument at a time or passing the 3 arguments at once, or allow the setup to force the user to have to use the 3 arguments at once version. This is accomplished with the minimum and maximum setup parameters.

The minimum setup parameter indicates the minimum amount of parameters the user can pass at a time. For the example above, the parameter could be set to 3 to force the user to have to pass the 3 arguments at once. When set to 1, the user will be able to pass a single parameter per option call.

The maximum setup parameter indicates the maximum amount of parameters the user can pass at a time. The option parser will leave any non option argument after the maximum in the remaining slice.

In go-getoptions this is accomplished with:

  • strSlice := opt.StringSlice(name, minArgs, maxArgs).

  • opt.StringSliceVar(&ptr, name, minArgs, maxArgs).

  • intSlice := opt.IntSlice(name, minArgs, maxArgs).

  • opt.IntSliceVar(&ptr, name, minArgs, maxArgs).

Additionally, in the case of integers, positive integer ranges are allowed. For example:

Instead of writing: csv --columns 1 2 3 or csv --columns 1 --columns 2 --columns 3

The input could be: csv --columns 1..3.

In go-getoptions this is currently enabled by default when using:

  • intSlice := opt.IntSlice(name, minArgs, maxArgs)

  • opt.IntSliceVar(&ptr, name, minArgs, maxArgs).

Options with key value arguments and multiple entries

This allows the user to save typing. For example:

Instead of writing: connection --server hostname=serverIP --server port=123 --client hostname=localhost --client port=456

The input could be: connection --server hostname=serverIP port=123 --client hostname=localhost port=456

In go-getoptions this is accomplished with:

  • strMap := opt.StringMap(name, minArgs, maxArgs).

  • opt.StringMapVar(&ptr, name, minArgs, maxArgs).

That covers a complete user interface that is flexible enough to accommodate most programs. The following are advanced features:

Stop parsing options when -- is passed

Useful when arguments start with dash - and you don’t want them interpreted as options.

In go-getoptions this is the default behaviour.

Stop parsing options when a command is passed

A command is assumed to be the first argument that is not an option or an argument to an option. When a command is found, stop parsing arguments and let a command handler handle the remaining arguments. For example:

program --opt arg command --subopt subarg

In the example above, --opt is an option and arg is an argument to an option, making command the first non option argument.

Additionally, when mixed with pass through, it will also stop parsing arguments when it finds the first unmatched option.

In go-getoptions this is accomplished with:

  • opt.SetUnknownMode(getoptions.Pass).

And can be combined with:

  • opt.SetRequireOrder().

Allow passing options and non-options in any order

Some option parsers force you to put the options before or after the arguments. That is really annoying!

In go-getoptions this is the default behaviour.

Allow pass through

Have an option to pass through unmatched options. Useful when writing programs with multiple options depending on the main arguments. The initial parser will only capture the help or global options and pass through everything else. Additional argument parsing calls are invoked on the remaining arguments based on the initial input.

In go-getoptions this is accomplished with:

  • opt.SetUnknownMode(getoptions.Pass).

Fail on unknown

The opposite of the above option. Useful if you want to ensure there are no input mistakes and force the application to stop.

In go-getoptions this is the default behaviour.

It can be explicitly set with:

opt.SetUnknownMode(getoptions.Fail).

Warn on unknown

Less strict parsing of options. This will warn the user that the option used is not a valid option but it will not stop the rest of the program.

In go-getoptions this is accomplished with:

  • opt.SetUnknownMode(getoptions.Warn).

Option aliases

Options should be allowed to have different aliases. For example, the same option could be invoked with --address or --hostname.

In go-getoptions, pass opt.Alias("my-alias") to any option. For example:

opt.BoolVar(&flag, "flag", false, opt.Alias("alias", "alias-2"))

Finally, to know with what alias an option was called with used opt.CalledAs(<name>).

Required options

Mark an option as required. Return an error if the option is not called.

In go-getoptions, pass opt.Required() to any option. For example:

opt.BoolVar(&flag, "flag", false, opt.Required())

Optionally, override the default error message with opt.Required(msg). For example:

opt.BoolVar(&flag, "flag", false, opt.Required("Missing --flag!"))

Incremental option

Some options can be passed more than once to increment an internal counter. For example:

command --v --v --v

Could increase the verbosity level each time the option is passed.

In go-getoptions this is accomplished with:

  • ptr := opt.Increment(name, default_value).

  • opt.IncrementVar(&ptr, name, default_value).

Additional types

The option parser could provide converters to additional types. The disadvantage of providing non basic types is that the option parser grows in size.

Not yet implemented in go-getoptions.

Options with optional arguments

With regular options, when the argument is not passed (for example: --level instead of --level=debug) you will get a Missing argument error. When using options with optional arguments, If the argument is not passed, the option will set the default value for the option type. For this feature to be fully effective in strong typed languages where types have defaults, there must be a means to query the option parser to determine whether or not the option was called or not.

In go-getoptions this is accomplished with:

  • ptr := opt.StringOptional(name, default_value).

  • ptr := opt.IntOptional(name, default_value).

  • ptr := opt.Float64Optional(name, default_value).

  • The above should be used in combination with opt.Called(name).

For example, for the following definition:

ptr := opt.StringOptional("level", "info")

  • If the option level is called with just --level, the value of *ptr is the default "info" and querying opt.Called("level") returns true.

  • If the option level is called with --level=debug, the value of *ptr is "debug" and querying opt.Called("level") returns true.

  • Finally, If the option level is not called, the value of *ptr is the default "info" and querying opt.Called("level") returns false.

Option flags that call a method internally

If all the flag is doing is call a method or function when present, then having a way to call that function directly saves the programmer some time.

Not yet implemented in go-getoptions.

Operation Modes

Notice how so far only long options (options starting with double dash --) have been mentioned. There are 3 main ways to handle short options (options starting with only one dash -).

The behaviour for long options (options starting with double dash --) is consistent across operation modes. The behaviour for short options (options starting with only one dash -) depends on the operation mode. The sections below show the different operation modes.

Normal Mode (default)

Given argument Interpretation

--opt

option: "opt", argument: nil

--opt=arg

option: "opt", argument: "arg" [1]


1. Argument gets type casted depending on option definition.

-opt

option: "opt", argument: nil

-opt=arg

option: "opt", argument: "arg" [2]


2. Argument gets type casted depending on option definition.

Bundling Mode

Set by defining opt.SetMode(getoptions.Bundling).

Given option Interpretation

--opt

option: "opt", argument: nil

--opt=arg

option: "opt", argument: "arg" [3]


3. Argument gets type casted depending on option definition.

-opt

option: "o", argument: nil
option: "p", argument: nil
option: "t", argument: nil

-opt=arg

option: "o", argument: nil
option: "p", argument: nil
option: "t", argument: "arg" [4]


4. Argument gets type casted depending on option definition.

Enforce Single Dash Mode

Set by defining opt.SetMode(getoptions.SingleDash).

Given option Interpretation

--opt

option: "opt", argument: nil

--opt=arg

option: "opt", argument: "arg" [5]


5. Argument gets type casted depending on option definition.

-opt

option: "o", argument: "pt" [6]


6. Argument gets type casted depending on option definition.

-opt=arg

option: "o", argument: "pt=arg" [7]


7. Argument gets type casted depending on option definition.

Biggest option parser misfeature - Automatically generate help

The biggest misfeature an option parser can have is to automatically generate the help message for the programmer. This seemingly helpful feature has caused most tools not to have proper man pages anymore and to have all verbose descriptions mixed in the help synopsis.

If you are writing a mid to large tool, don’t be lazy, write a man page for your program! If you are looking for options, asciidoctor has a manpage backend that can generate manpages written in the Asciidoc markup.

For the help synopsis, however, use the automated help. It even shows when an option can be set with environment variables.

For example, the following is a script using the built in help:

$ ./aws-configure -h
NAME:
    aws-configure - Generate default ~/.aws/config and ~/.aws/credentials configuration.

        When a role is passed, it allows the use of the role in the default profile.

        NOTE: Remember to unset AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY after use.

SYNOPSIS:
    aws-configure --access-key-id <string> --region <string>
                  --secret-access-key <string> [--debug] [--help|-?]
                  [--output-dir <string>] [--role-arn <string>] [--version|-V]
                  [<args>]

REQUIRED PARAMETERS:
    --access-key-id <string>        AWS Access Key ID. (env: AWS_ACCESS_KEY_ID)

    --region <string>               Default Region. (env: AWS_DEFAULT_REGION)

    --secret-access-key <string>    AWS Secret Access Key. (env: AWS_SECRET_ACCESS_KEY)

OPTIONS:
    --debug                         (default: false)

    --help|-?                       (default: false)

    --output-dir <string>           Where to place the config and credentials file. (default: "/home/david/.aws")

    --role-arn <string>             Role ARN. (default: "", env: AWS_ROLE_ARN)

    --version|-V                    (default: false)

And below is the output of the automated help of a program with multiple commands:

$ menu
SYNOPSIS:
    menu [--config <string>] [--debug] [--help|-?] [--profile <string>]
         [--region <string>] [--role <string>] [--version|-V] <command> [<args>]

COMMANDS:
    docker       docker tasks
    help         Use 'menu help <command>' for extra details.
    instance     Actions on your deployed instances
    terraform    Run terraform commands from inside the container

OPTIONS:
    --config <string>     (default: "config.yml")

    --debug               (default: false)

    --help|-?             (default: false)

    --profile <string>    (default: "default")

    --region <string>     (default: "us-west-2")

    --role <string>       (default: "")

    --version|-V          (default: false)

Use 'menu help <command>' for extra details.

Command behaviour

This section describes how the parser resolves ambiguities between the program and the command.

Given a definition like:

func main() {
	var profile, password string
	opt := New()
	opt.SetUnknownMode(Pass)
	opt.StringVar(&profile, "profile", "")
	command := NewCommand()
	command.StringVar(&password, "password", "")
	opt.Command(command.Self("command", "").SetCommandFn(commandFn))
	remaining, err := opt.Parse(os.Args[1:])
	...
	err = opt.Dispatch("help", remaining)
	...
}
func commandFn(opt *getoptions.GetOpt, args []string) error {
	args, err := opt.Parse(remaining)
	...
}

There is an option at the parent, profile and one at the command, password. Passing --p <arg> is ambiguous and results in an error. At minimum, --pr <arg> and --pa <arg> are required.

Given a definition like:

func main() {
	var profile, password string
	opt := New()
	opt.SetUnknownMode(Pass)
	opt.StringVar(&profile, "profile", "")
	command := NewCommand()
	command.StringVar(&password, "password", "", opt.Alias("p"))
	opt.Command(command.Self("command", "").SetCommandFn(commandFn))
	remaining, err := opt.Parse(os.Args[1:])
	...
	err = opt.Dispatch("help", remaining)
	...
}
func commandFn(opt *getoptions.GetOpt, args []string) error {
	args, err := opt.Parse(remaining)
	...
}

There is an option at the parent, profile and one at the command, password with alias p. Passing --p <arg> at the parent results in the parent opt.Parse call to leave the --p <arg> option unhandled and leave it in the remaining slice. The opt.Dispatch call gets the -p <arg> option and throws an error. At minimum, --pr <arg> is required to call profile at the parent and command options must be passed after the command declaration.

For example, the calls below is correct:

$ ./program -pr <profile> command -p <password>
$ ./program command -pr <profile> -p <password>

But the following one is incorrect:

./program -pr <profile> -p <password> command

Environment Variables Support

Initial support for environment variables has been added.

Currently, only: - opt.Bool and opt.BoolVar - opt.String, opt.StringVar, opt.StringOptional, and opt.StringVarOptional - opt.Int, opt.IntVar, opt.IntOptional, and opt.IntVarOptional - opt.Float64, opt.Float64Var, opt.Float64Optional, and opt.Float64VarOptional

To use it, set the option modify function to opt.GetEnv. For example:

var profile string
opt.StringVar(&profile, "profile", "default", opt.GetEnv("AWS_PROFILE"))

Or:

profile := opt.String("profile", "default", opt.GetEnv("AWS_PROFILE"))
Note
Non supported option types behave with a No-Op when opt.GetEnv is defined.

When using opt.GetEnv with opt.Bool or opt.BoolVar, only the words "true" or "false" are valid. They can be provided in any casing, for example: "true", "True" or "TRUE".

Note
For numeric values, opt.Int and opt.Float64 and their derivatives, environment variable string conversion errors are ignored and the default value is assigned.

Possible Env Variable Roadmap

The Roadmap isn’t clear given that there might not be enough value in implementing all of them.

  • Handle opt.Int and opt.Float64 errors.

    StringSlice and StringSliceVar

    Comma separated? ← Most likely

    Comma space separated? Proper CSV parsing to allow comma escaping?

    IntSlice and IntSliceVar

    Comma separated?

    StringMap and StringMapVar

    Comma separated key=value?

ROADMAP

  • Create new error description for errors when parsing integer ranges (1..3).

  • Option that runs a function?

  • Case insensitive matching.

  • Option values in the bundle: -h1024w800-h 1024 -w 800

  • prefix and prefix_pattern. The string that starts options. Defaults to "--" and "-" but could include "/" to support Win32 style argument handling.

  • Supports argument dividers other than '='. For example: You could define ':' and use --string=mystring, --string:mystring and --string mystring.

  • All other Perl’s Getopt::Long goodies that seem reasonable to add!

  • Remove need for opt.HelpCommand("") when dispatch is defined.

    Currently we have to define the opt.Bool("help", false) flag, then opt.HelpCommand(""), and finally opt.Dispatch(ctx, "help", remaining). opt.HelpCommand is redundant. Additionally opt.HelpCommand has the help command name hardcoded in it.

License

This file is part of go-getoptions.

Copyright © 2015-2020 David Gamba Rios

This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

Issues
  • Making StringMap/StringMapVar optional?

    Making StringMap/StringMapVar optional?

    Greetings, this is my favorite of the Go command-line parsing mechanisms. One question though: I noticed that StringMap/StringMapVar have a minimum of 1 entry. In my case I intend to fall back to a configuration file for map[string]string values that aren't explicitly provided on the command-line. Does this sound reasonable or am I making an incorrect usage assumption?

    question 
    opened by SlowPokeInTexas 2
  • question about non dash argument

    question about non dash argument

    Hi, is there a way to allow a commandline with a first argument without a dash ? For exemple : ./myprogram init -v 4 -d --long something or ./myprogram build -v 5 [...] ?

    question 
    opened by crownedgrouse 2
  • Fix spelling mistake on DepthFirstSort mispelled as DephFirstSort

    Fix spelling mistake on DepthFirstSort mispelled as DephFirstSort

    Hello David :). Was snooping around and saw a spelling mistake. Thought I'd go ahead and make a PR. Let me know if anything needs to be changed.

    I've run the tests locally and it looks good, and I've compiled some of your examples (dag, mygit) and they seem to work.

    opened by bseto 2
  • Startup panic when alias matches the beginning of preexisting option

    Startup panic when alias matches the beginning of preexisting option

    Code to reproduce it:

        opt = New()
        opt.Bool("fleg", false)
        opt.Bool("flag", false, "f")
        _, err = opt.Parse([]string{})
    

    Output:

    panic: Alias 'f' is already defined for option 'fleg'

    Workaround: Define the option with the alias before the one without.

        opt = New()
        opt.Bool("flag", false, "f")
        opt.Bool("fleg", false)
        _, err = opt.Parse([]string{})
    
    opened by DavidGamba 0
  • Fix for issue #1

    Fix for issue #1

    fix for #1

    opened by DavidGamba 0
Releases(v0.23.0)
  • v0.23.0(Oct 21, 2020)

    As the releases before, this release has 100% test coverage. Tested with Go 1.14 and Go 1.15.

    Feature Updates

    • Introduce Float64Optional and Float64VarOptional to have complete method parity for String, Int and Float64 types.

    • Support multi-line command descriptions.

    • Add GetEnv support for missing single option types:

      • Int, IntVar, IntOptional, IntVarOptional
      • StringOptional, StringVarOptional
      • Float64, Float64Var, Float64Optional, Float64VarOptional

      NOTE: For numeric values, opt.Int and opt.Float64 and their derivatives, environment variable string conversion errors are ignored and the default value is assigned.

    Source code(tar.gz)
    Source code(zip)
  • v0.22.0(Oct 11, 2020)

    As the releases before, this release has 100% test coverage. Tested with Go 1.14 and Go 1.15.

    Bug fix

    Fix completion issues where a completion that works when starting to complete from scratch fails when some args are deleted.

    Fixed by changing the exit status when generating completions from 1 to 124. Exit 124 means programmable completion restarts from the beginning, with an attempt to find a new compspec for that command.

    Feature Removal

    Removing negatable flags NBool and NBoolVar. A feature that adds a bunch of complexity for very little value and prevents reading environment variables into booleans.

    New Features

    • opt.GetEnv Is now supported when using opt.Bool and opt.BoolVar. Previously only opt.String and opt.StringVar were supported.

      When using opt.GetEnv with opt.Bool or opt.BoolVar, only the words "true" or "false" are valid. They can be provided in any casing, for example: "true", "True" or "TRUE".

    • opt.Dispatch now automatically handles the help flag. The help flag needs to be defined at the top level. When the help flag is called and handled by a command opt.Dispatch now returns an error of type getoptions.ErrorHelpCalled.

      For example:

    func main() {
    	os.Exit(program())
    }
    
    func program() int {
    	opt := getoptions.New()
    	opt.Bool("help", false, opt.Alias("?"))    // Define the help flag as "--help" with alias "-?"
    	list := opt.NewCommand("list", "list stuff").SetCommandFn(listRun)
    	list.Bool("list-opt", false)
    	opt.HelpCommand("")
    	remaining, err := opt.Parse(os.Args[1:])
    	if err != nil {
    		fmt.Fprintf(os.Stderr, "ERROR: %s\n", err)
    		os.Exit(1)
    	}
    
    	ctx, cancel, done := opt.InterruptContext()
    	defer func() { cancel(); <-done }()
    
    	err = opt.Dispatch(ctx, "help", remaining) // Use the same help flag "help".
    	if err != nil {
    		if errors.Is(err, getoptions.ErrorHelpCalled) {
    			return 1
    		}
    		fmt.Fprintf(os.Stderr, "ERROR: %s\n", err)
    		return 1
    	}
    	return 0
    }
    

    Now, calling program list --help or program list -? prints the help for the list command as well as calling program help list.

    Source code(tar.gz)
    Source code(zip)
  • v0.21.0(Aug 15, 2020)

    As the releases before, this release has 100% test coverage.

    Drop support for Go versions before 1.14

    Dropping support for Go 1.10, 1.11, 1.12 and 1.13 to leverage new errors and testing features.

    In particular The errors.Is and errors.As features greatly simplify error testing and handling and are used in the new DAG build system.

    New Feature

    Introduces a new Directed Acyclic Graph Build System.

    The build system is a separate import package: import "github.com/DavidGamba/go-getoptions/dag"

    Documentation can be found in its own README.

    Source code(tar.gz)
    Source code(zip)
  • v0.20.2(Jul 20, 2020)

    As the releases before, this release has 100% test coverage.

    Deprecration WARNING

    Support for Go 1.10, 1.11 and 1.12 will be dropped in a future release. The errors.Is and errors.As features greatly simplify error testing and handling and will likely be introduced in the near future.

    Improve autocompletion behaviour.

    • Pass autocompletion entries to children.

    From v0.20.0 all options started being passed to children commands. Their completion entries were missing.

    • Separate internal option completion between flags that don't expect an argument and options that do. When an option that expects an argument is found, the given argument won't break the completion chain. Only one argument is supported per option.

    • Don't break autocompletion chain when there is an option in the chain that accepts an argument with =. For example: program --profile=dev <tab><tab> will show completions for program.

    Source code(tar.gz)
    Source code(zip)
  • v0.20.1(Jun 16, 2020)

    As the releases before, this release has 100% test coverage.

    • Improve autocompletion behaviour:

      Break words in COMP_LINE by matching against multiple spaces \s+ instead of a single one.

    Source code(tar.gz)
    Source code(zip)
  • v0.20.0(Jun 9, 2020)

    As the releases before, this release has 100% test coverage.

    Method Deprecation

    • Deprecate opt.SetOption

    Since the introduction of opt.NewCommand(name, description string) there is a proper parent child relationship between commands. There is no need to hack passing desired options to the child command, instead, now all options are automatically propagated to the child.

    This has the side benefit to make the automated help clearer by listing all options that previously where only listed in one of the parent levels.

    To update, remove calls to opt.SetOption, for example:

     	opt := getoptions.New()
     	opt.Bool("help", false, opt.Alias("?"))
     	opt.Bool("debug", false)
     	opt.SetRequireOrder()
     	opt.SetUnknownMode(getoptions.Pass)
     	list := opt.NewCommand("list", "list stuff")
    -	list.SetOption(opt.Option("help"), opt.Option("debug")).SetCommandFn(listRun)
    +	list.SetCommandFn(listRun)
     	list.Bool("list-opt", false)
     	opt.HelpCommand("")
     	remaining, err := opt.Parse([]string{"list"})
    

    Feature Update

    • Automatically run opt.Parse when calling opt.Dispatch.

    When defining a new command, we define the function that the command will run with command.SetCommandFn(commandFunction). If the command is passed in the command line, opt.Dispatch calls the command function. Previously, opt.Dispatch wasn't automatically calling opt.Parse in the command function so the first thing that every command function had to do was a call to parse.

    For example:

    func main() {
    	opt := getoptions.New()
    	list := opt.NewCommand("list", "list stuff")
    	list.SetCommandFn(listRun)
    	opt.HelpCommand("")
    	remaining, err := opt.Parse(os.Args[1:])
    	if err != nil {
    		...
    	}
    
    	err = opt.Dispatch(context.Background(), "help", remaining)
    	if err != nil {
    		...
    	}
    }
    
    func listRun(ctx context.Context, opt *getoptions.GetOpt, args []string) error {
    	remaining, err := opt.Parse(args)
    	if err != nil {
    		...
    	}
    	// Function code here
    }
    

    Now, the call opt.Parse is automated by opt.Dispatch so the command function is simplified to:

    func listRun(ctx context.Context, opt *getoptions.GetOpt, args []string) error {
    	// Function code here
    }
    

    Where the received opt has the arguments already parsed and the received args is the remaining arguments that didn't match any option.

    Source code(tar.gz)
    Source code(zip)
  • v0.19.0(Apr 4, 2020)

    As the releases before, this release has 100% test coverage.

    Update

    • opt.GetEnv now satisfies opt.Required:

    When an environment variable that matches the variable from opt.GetEnv is set, opt.GetEnv will set opt.Called to true and will set opt.CalledAs to the name of the environment variable used. In other words, when an option is required, opt.Required is set, opt.GetEnv satisfies that requirement.

    • opt.GetEnv environment variable now shows in help output.

    Example:

    REQUIRED PARAMETERS:
        --access-key-id <string>        AWS Access Key ID. (env: AWS_ACCESS_KEY_ID)
    
        --role-arn <string>             Role ARN. (env: AWS_ROLE_ARN)
    
        --secret-access-key <string>    AWS Secret Access Key. (env: AWS_SECRET_ACCESS_KEY)
    
    OPTIONS:
        --region <string>               Default Region. (default: "us-west-2", env: AWS_DEFAULT_REGION)
    
    Source code(tar.gz)
    Source code(zip)
  • v0.18.0(Mar 24, 2020)

    As the releases before, this release has 100% test coverage.

    This release adds initial support for Environment Variables and adds lots of GoDoc examples.

    New Features

    • Initial support for environment variables has been added.

    Currently, only opt.String and opt.StringVar are supported.

    To use it, set the option modify function to opt.GetEnv. For example:

    var profile string
    opt.StringVar(&profile, "profile", "default", opt.GetEnv("AWS_PROFILE"))
    

    Or:

    profile := opt.String("profile", "default", opt.GetEnv("AWS_PROFILE"))
    

    NOTE: Non supported option types behave with a No-Op when opt.GetEnv is defined.

    Minor changes

    • Change opt.Dispatch signature to clarify the actual use of the variable. Additionally, actually use the variable, before it was hardcoded to "help".
    -func (gopt *GetOpt) Dispatch(ctx context.Context, helpOptionName string, args []string) error
    +func (gopt *GetOpt) Dispatch(ctx context.Context, helpCommandName string, args []string) error
    
    Source code(tar.gz)
    Source code(zip)
  • v0.17.0(Oct 24, 2019)

    As the releases before, this release has 100% test coverage.

    This release keeps on the work of removing the kinks around subcommands. An example showing subcommands can be found in ./examples/mygit.

    It also introduces the use of context to propagate cancelation signals, etc. to the child commands.

    Finally, it introduces a new helper that captures interrupts (for example Ctrl-C) and returns a top level context.

    Breaking changes

    • Refactor NewCommmand as a method. This will allow the built-in help to have information about the parent. It might also help with autocompletion.

    • Change sigature to opt.NewCommand(name, description string). It takes a name and description now.

    • Change signature of CommandFn to have a context as the first argument. It will allow the parent to propagate cancelation signals, etc. to the child commands. This change goes along a change to the helper opt.Dispatch to also have a context as the first argument.

    Updating:

    -   list := getoptions.NewCommand().Self("list", "list instances").
    +   list := opt.NewCommand("list", "list instances").
            SetOption(parent.Option("help"), parent.Option("debug")).
            SetCommandFn(runInstanceList)
        list.StringSlice("tag", 1, 99, opt.Alias("t"),
            opt.Description("Any AWS tags you want to list"))
    -   opt.Command(list)
    
     ...
    
    -   err = opt.Dispatch("help", remaining)
    +   err = opt.Dispatch(context.Background(), "help", remaining)
    
     ...
    
    -func runInstanceList(opt *getoptions.GetOpt, args []string) error {
    +func runInstanceList(ctx context.Context, opt *getoptions.GetOpt, args []string) error {
    

    New Features

    • Introduce opt.InterruptContext(), a helper that returns a top level context that captures interrupt signals (os.Interrupt, syscall.SIGHUP, syscall.SIGTERM). An example can be found in ./examples/mygit.
    Source code(tar.gz)
    Source code(zip)
  • v0.16.0(Sep 23, 2019)

    As the releases before, this release has 100% test coverage.

    Fixes

    • Bug Fix: Update opt.Dispatch not to handle --help option. It was preventing the help option to reach the commands.

    New Features

    • Introduce opt.HelpSynopsisArgs(string) method to allow overriding the default args description. The current default description is [<args>].

    Minor changes

    • Make SetMode, SetUnknownMode, SetRequireOrder and SetMapKeysToLower chainable.
    Source code(tar.gz)
    Source code(zip)
  • v0.15.0(Aug 29, 2019)

    As the releases before, this release has 100% test coverage.

    Change workflow to deal with ambiguities between parent and child.

    For example, the root may have option --profile and the command the option --password with alias -p. If -p is passed, the parent would uncorrectly be matched.

    For the parent to properly handle ambiguities with its children, it needs to have knowledge of them. A new getoptions.NewCommand has been introduced.

    To help with the verbosity of handling all the commands, a new Dispatch method is introduced, it will call a command's function defined with the new SetCommandFn method.

    New Features

    • Introduce getoptions.NewCommand() to declare commands and clearly separate their role from the main getoptions.New().

    • Introduce command.SetCommandFn(fn CommandFn) to declare a commands function callback.

    • Introduce opt.Dispatch(helpOptionName string, args []string) to automatically handle dispatching to the CommandFn based on the cli input.

    • Make options unambiguous with commands. For example, the root may have option --profile and the command the option --password with alias -p. If -p is passed, the parent would uncorrectly be matched.

    • Introduce new error indicating which options are getting matched with ambiguous options.

    • Add getoptions.HelpCommand() to have an automated help command. It adds completions for all other commands automatically.

    Minor changes

    • Fix unknown option warning formatting. Each warning has its own line and it is preceded by WARNING:.

    • Minor README updates... New features need proper documentation.

    • Minor automated help changes.

    Source code(tar.gz)
    Source code(zip)
  • v0.14.1(Jul 28, 2019)

  • v0.14.0(Jul 28, 2019)

    As the releases before, this release has 100% test coverage.

    This release introduces bash completion by default and works out many kinks around subcommands. An example showing subcommands can be found in ./examples/mygit.

    Breaking changes

    • Remove support for Go < v1.10 (v1.5 - v1.9).

    • Rename getoptions.Option to getoptions.Value. WARNING: A new getoptions.Option method is has been introduced, but the new one returns *option.Option instead.

    • Change the argument of opt.SetMode and opt.SetUnknownMode from a string to a getoptions.Mode and getoptions.UnknownMode type. Makes it easier to autodiscover valid arguments for the method.

    • Refactor section help methods into the main opt.Help one.

    - opt.HelpName()
    + opt.Help(getoptions.HelpName)
    
    - opt.HelpSynopsis()
    + opt.Help(getoptions.HelpSynopsis)
    
    - opt.HelpCommandList()
    + opt.Help(getoptions.HelpCommandList)
    
    - opt.HelpOptionList()
    + opt.Help(getoptions.HelpOptionList)
    

    To print all the sections of the automated help, continue to use opt.Help().

    New Features

    • Implement bash completion by default.

    Add the following to your .bashrc: complete -o default -C "/binary/location/myscript" myscript

    • New getoptions.Option method that returns *option.Option. In combination with the new getoptions.SetOption it allows to pass options from parent to subcommand.

    • Add getoptions.CustomCompletion method. Given a list, it will add the elements of the list to the completion alternatives.

    • Add getoptions.StringMapVar method.

    Minor changes

    • Pad SYNOPSIS content with 4 spaces. • Add Self method to populate help NAME section.

    Backend changes

    • Refactor the code into more modular pieces.

    Source code(tar.gz)
    Source code(zip)
  • v0.13.0(Mar 2, 2019)

    As the releases before, this release has 100% test coverage.

    New Features

    • Experimental implementation of help messages.
    • Show used alias in errors for single options (not slice or maps).
    • Add opt.CalledAs method to know how the option was called.

    Minor changes

    • Add example script.
    • Add golang 1.12 to test suite.
    Source code(tar.gz)
    Source code(zip)
  • v0.12.0(Feb 19, 2019)

    As the releases before, this release has 100% test coverage.

    Breaking change: Update function signatures

    Change all function signatures from:

    XVar(p *bool, name string, def bool, aliases ...string)

    To:

    XVar(p *bool, name string, def bool, fns ...ModifyFn)

    This change allows to pass different functions to the option that will modify single option behaviour and will allow for multiple features without future breaking changes in the function signature.

    As part as this change, a new function, opt.Alias is added to support previous functionality.

    To update, change the aliases from a list of aliases as the variadic last argument to a list of aliases passed to the opt.Alias function. For example:

    - opt.BoolVar(&flag, "flag", false, "f", "alias2")
    + opt.BoolVar(&flag, "flag", false, opt.Alias("f", "alias2"))
    

    New Features

    • Add opt.Alias option modifier to assign option aliases.
    • Add opt.Required option modifier to indicate if an option is required.
    Source code(tar.gz)
    Source code(zip)
  • v0.11.0(Aug 16, 2017)

    As the releases before, this release has 100% test coverage.

    Fix go tip case sensitive import path:

    • davidgamba -> DavidGamba

    Deprecate and rename redundant methods:

    • StringSlice is redundant with StringSliceMulti. Calling: StringSlice(name, aliases...) Is the same as Calling: StringSliceMulti(name, 1, 1, aliases...) Consolidate API to: StringSlice(name, min, max, aliases...)

    • StringMap is redundant with StringMapMulti. Calling: StringMap(name, aliases...) Is the same as Calling: StringMapMulti(name, 1, 1, aliases...) Consolidate API to: StringMap(name, min, max, aliases...)

    • Rename IntSliceMulti to IntSlice.

    New features:

    • Add StringSliceVar and IntSliceVar methods.

    • Add option to SetMapKeysToLower.

    Source code(tar.gz)
    Source code(zip)
Owner
David Gamba
David Gamba
CONTRIBUTIONS ONLY: A Go (golang) command line and flag parser

CONTRIBUTIONS ONLY What does this mean? I do not have time to fix issues myself. The only way fixes or new features will be added is by people submitt

Alec Thomas 3.1k Jul 26, 2021
:cherry_blossom: A command-line fuzzy finder

fzf is a general-purpose command-line fuzzy finder. It's an interactive Unix filter for command-line that can be used with any list; files, command hi

Junegunn Choi 38k Jul 25, 2021
Query, update and convert data structures from the command line. Comparable to jq/yq but supports JSON, TOML, YAML, XML and CSV with zero runtime dependencies.

dasel Dasel (short for data-selector) allows you to query and modify data structures using selector strings. Comparable to jq / yq, but supports JSON,

Tom Wright 955 Jul 24, 2021
Argparse for golang. Just because `flag` sucks

Golang argparse Let's be honest -- Go's standard command line arguments parser flag terribly sucks. It cannot come anywhere close to the Python's argp

Alexey Kamenskiy 347 Jul 23, 2021
A versatile library for building CLI applications in Go

mow.cli Package cli provides a framework to build command line applications in Go with most of the burden of arguments parsing and validation placed o

Jawher Moussa 741 Jul 18, 2021
CLI - A package for building command line app with go

Command line interface Screenshot Key features Lightweight and easy to use. Defines flag by tag, e.g. flag name(short or/and long), description, defau

王仕晋 587 Jul 18, 2021
The blackbean is a command tool for elasticsearch operations by using cobra.

The blackbean is a command tool for elasticsearch operations by using cobra. Besides, blackbean is the name of my lovely French bulldog.

null 11 Jul 18, 2021
Kong is a command-line parser for Go

Kong is a command-line parser for Go Introduction Help Help as a user of a Kong application Defining help in Kong Command handling Switch on the comma

Alec Thomas 589 Jul 24, 2021
HAProxy configuration parser

HAProxy configuration parser autogenerated code if you change types/types.go you need to run go run generate/go-generate.go $(pwd) Contributing For co

haproxytech 60 Jul 21, 2021
Integrated console application library, using Go structs as commands, with menus, completions, hints, history, Vim mode, $EDITOR usage, and more ...

Gonsole - Integrated Console Application library This package rests on a readline console library, (giving advanced completion, hint, input and histor

null 12 Jun 28, 2021
Source code editor in pure Go.

Editor Source code editor in pure Go. About This is a simple but advanced source code editor As the editor is being developed, the rules of how the UI

Jorge Miguel Pinto 226 Jun 29, 2021
A Commander for modern Go CLI interactions

Cobra is both a library for creating powerful modern CLI applications as well as a program to generate applications and command files. Cobra is used i

Steve Francia 22.5k Jul 26, 2021
Building powerful interactive prompts in Go, inspired by python-prompt-toolkit.

go-prompt A library for building powerful interactive prompts inspired by python-prompt-toolkit, making it easier to build cross-platform command line

Masashi Shibata 4k Jul 27, 2021
An easy to use menu structure for cli applications that prompts users to make choices.

WMenu Package wmenu creates menus for cli programs. It uses wlog for its interface with the command line. It uses os.Stdin, os.Stdout, and os.Stderr w

Will Dixon 139 Jul 16, 2021