Documentation
¶
Overview ¶
Package getopt implements a command line parser. It can handle POSIX-style flags as well as GNU-style long options.
The API design is inspired and highly influenced by the venerable getopt — hence the name! Although, an effort was made to tweak the design to conform with common Go idioms. For example, Getopt does not rely on global state, and the error interface is used throughout for all error cases.
Command line flag syntax ¶
Short options consist of a single dash followed by a character. They can either take an argument or not, dependig on the programmer's choice.
-a -b <argument> -b<argument> # alternate spelling without space
For brevity, multiple short options can be combined into a single argument. When doing so, only the last option of the group may take an argument.
-abcd # equivalent to -a -b -c -d -ef <argument> # equivalent to -e -f <argument> -ef<argument> # alternate spelling
Long options start with two dashes followed by one or more characters. Arguments are either part of the same argument, separated by an equal sign "=", or passed as the next argument.
--long-option --another-option <argument> # space --another-option=<argument> # equal sign
Optional arguments are supported as well. In that case an option can be used either with or without an argument. Optional arguments must be passed conjoined to the same argument as the option. The element in the list after the option flag is not considered for the sake of optional arguments.
-v -v<argument> -v <argument> # invalid! --verbose --verbose=<argument> --verbose <argument> # invalid!
Parsing of options can be stopped with a double-dash "--". Arguments after it will not be considered options, even if they start with a dash.
--this-is-an-option -- --this-is-not
Some command line parsers, like GNU's getopt_long for C and pflag for Go, support mixing options and the remaining arguments. That behaviour does not conform to the POSIX getopt specification and is not supported by this package.
--this-is=an-option some-argument --this-is-not-an-option
Usage ¶
Contrary to many other command line flag parsers for Go, options and information about them, like whether they take an argument and the type of the argument, are not defined upfront. Instead Getopt loops over the options it finds and calls a handler function for each option. If Option.Arg is called in the handler, the option takes an argument. Otherwise the option does not take an argument.
Option.Arg returns the argument as a string, but there some helper functions available that allow to retrieve the argument and parse it in a single step. Signed and unsigned integers, floats, and types implementing the TextUnmarshaler interface (with Option.ArgTextVar) are supported. Other types have to be parsed manually in the handler function. If there is high demand for other types, helpers can be added to this package.
There are also *Var (variable) variants for all argument access methods. While Option.Arg returns a tuple of the value and an error, Option.ArgVar takes a pointer to a variable that it sets on success. Its sole return value is an error, which allows to retrieve the argument, set a variable and return the error (or lack thereof) in a single line. Otherwise the behaviour is identical between both methods.
For optional arguments Option.HasConjoinedArg can be used to determine if the current option has an argument value defined within the same argument. A conjoined argument to a long option that's not consumed by the handler causes Getopt to abort parsing and return an error. For a short option, parsing will continue and the conjoined argument will be re-interpreted as other short options.
args, err := getopt.Getopt(os.Args[1:], func(opt *getopt.Option) error {
switch opt.Name() {
case "-a", "--all":
all = true
return nil
case "-v", "--verbose":
if opt.HasConjoinedArg() {
return opt.ArgIntVar(&verbosity)
} else {
verbosity++
return nil
}
}
return opt.Unknown()
})
Getopt features a full-fledged example.
Why? ¶
Go ships with the flag package, which implements a command line parser. However, it does not implement POSIX-style flags, which are so common in Unix (or Unix-like) environments. Instead, it implements a much less common, simplified syntax (only one type of options, optional arguments are only supported for boolean options, ...).
While I see the appeal of a simplified command line flag syntax and the cleaner implementation that goes with it, POSIX and GNU-style options provide certain benefits over the simplified syntax:
- Multiple options can be combined in the same argument. This might not matter for occasionally used programs or when writing scripts, but it's unbeatable in typing efficiency for interactive use, especially for often used commands.
- Options can have a long and a short name, e.g. -a and --all. This allows to use the longer, descriptive, name for use in scripts, without compromising interactive use.
- They play nice with and are consistent with the Unix environment. There are POSIX/GNU-style option parsers for almost all programming languages.
- People are used to this style of options and expect their use. There should be a really good reason when deviating from that expectation.
Contrary to many other command line flag parsers for Go, options are not defined upfront. That approach can lead to slightly longer code in simple cases, with the biggest downside that the usage string has to be written by hand, as it can't be derived from a declarative flag definition. However, it allows for a lot of flexibility in parsing the options. Custom argument types are as easy to implement as parsing the value in the handler function. Additionally, it makes it easy to take the order of options into account or change the meaning of flags that follow them. e.g. setting the output format to MP3 "unlocks" format specific options, like setting the bitrate or the stereo mode.
Alternatives ¶
If you are looking for a drop-in replacement for Go's flag package that implements POSIX-style flags, you might consider using pflag. It also supports interspersing flags with arguments, but the feature can also be turned off.
Even if you care about standard flags, for quick-and-dirty scripts or for internal tools flag might be good enough and picking up a dependency might not be worth it.
Index ¶
- func Getopt(args []string, fn func(*Option) error) ([]string, error)
- type Option
- func (opt *Option) Arg() (string, error)
- func (opt *Option) ArgFloat32() (float32, error)
- func (opt *Option) ArgFloat32Var(val *float32) error
- func (opt *Option) ArgFloat64() (float64, error)
- func (opt *Option) ArgFloat64Var(val *float64) error
- func (opt *Option) ArgInt() (int, error)
- func (opt *Option) ArgInt8() (int8, error)
- func (opt *Option) ArgInt8Var(val *int8) error
- func (opt *Option) ArgInt16() (int16, error)
- func (opt *Option) ArgInt16Var(val *int16) error
- func (opt *Option) ArgInt32() (int32, error)
- func (opt *Option) ArgInt32Var(val *int32) error
- func (opt *Option) ArgInt64() (int64, error)
- func (opt *Option) ArgInt64Var(val *int64) error
- func (opt *Option) ArgIntVar(val *int) error
- func (opt *Option) ArgTextVar(val encoding.TextUnmarshaler) error
- func (opt *Option) ArgUint() (uint, error)
- func (opt *Option) ArgUint8() (uint8, error)
- func (opt *Option) ArgUint8Var(val *uint8) error
- func (opt *Option) ArgUint16() (uint16, error)
- func (opt *Option) ArgUint16Var(val *uint16) error
- func (opt *Option) ArgUint32() (uint32, error)
- func (opt *Option) ArgUint32Var(val *uint32) error
- func (opt *Option) ArgUint64() (uint64, error)
- func (opt *Option) ArgUint64Var(val *uint64) error
- func (opt *Option) ArgUintVar(val *uint) error
- func (opt *Option) ArgVar(val *string) error
- func (opt *Option) HasConjoinedArg() bool
- func (opt *Option) InvalidArgument(err error) error
- func (opt *Option) InvalidArgumentStr(msg string) error
- func (opt *Option) Name() string
- func (opt *Option) Unknown() error
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Getopt ¶
Getopt loops over the options it finds and calls the handler function for each option. It returns an error when the handler returns an error, or when the conjoined argument of a long option has not been consumed by the handler.
Example ¶
package main
import (
"fmt"
"net/netip"
"os"
"gitlab.com/natano/getopt"
)
const usage = `example-command [-abc] [-v[<verbosity]] [--bind <address>] <args>...
Options:
-a, --all include all files
--bind <address> bind to provided IP address
-v, --force ignore nonexistent files
-v, --verbosity[=<verbosity>] verbose output
`
func main() {
// Usually you would use os.Argv[1:] instead.
osArgs := []string{"-a", "-v7", "--bind=127.0.0.1", "some", "files"}
all := false
verbosity := 0
var ipAddr netip.Addr
args, err := getopt.Getopt(osArgs, func(opt *getopt.Option) error {
switch opt.Name() {
case "-h", "--help":
fmt.Fprint(os.Stdout, usage)
os.Exit(0)
case "-a", "--all":
all = true
return nil
case "--bind":
return opt.ArgTextVar(&ipAddr)
case "-v", "--verbose":
if opt.HasConjoinedArg() {
return opt.ArgIntVar(&verbosity)
} else {
verbosity++
return nil
}
}
return opt.Unknown()
})
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
fmt.Fprint(os.Stderr, usage)
os.Exit(1)
}
fmt.Printf("all: %t\n", all)
fmt.Printf("bind: %s\n", ipAddr)
fmt.Printf("verbosity: %d\n", verbosity)
fmt.Printf("args: %#v\n", args)
}
Output: all: true bind: 127.0.0.1 verbosity: 7 args: []string{"some", "files"}
Types ¶
type Option ¶
type Option struct {
// contains filtered or unexported fields
}
Option represents one short or long option found by Getopt.
func (*Option) Arg ¶
Arg consumes the argument and returns it. An error is returned in case there is no argument.
func (*Option) ArgFloat32 ¶
ArgFloat32 consumes the argument and returns the parsed value. An error is returned in case there is no argument or the argument is not a valid 32-bit float.
func (*Option) ArgFloat32Var ¶
ArgFloat32Var consumes the argument and stores the parsed value in the variable pointed to by val. An error is returned in case there is no argument or the argument is not a valid 32-bit float.
func (*Option) ArgFloat64 ¶
ArgFloat64 consumes the argument and returns the parsed value. An error is returned in case there is no argument or the argument is not a valid 64-bit float.
func (*Option) ArgFloat64Var ¶
ArgFloat64Var consumes the argument and stores the parsed value in the variable pointed to by val. An error is returned in case there is no argument or the argument is not a valid 64-bit float.
func (*Option) ArgInt ¶
ArgInt consumes the argument and returns the parsed value. An error is returned in case there is no argument or the argument is not a valid base-10 integer.
func (*Option) ArgInt8 ¶
ArgInt8 consumes the argument and returns the parsed value. An error is returned in case there is no argument or the argument is not a valid base-10, 8-bit integer.
func (*Option) ArgInt8Var ¶
ArgInt8Var consumes the argument and stores the parsed value in the variable pointed to by val. An error is returned in case there is no argument or the argument is not a valid base-10, 8-bit integer.
func (*Option) ArgInt16 ¶
ArgInt16 consumes the argument and returns the parsed value. An error is returned in case there is no argument or the argument is not a valid base-10, 16-bit integer.
func (*Option) ArgInt16Var ¶
ArgInt16Var consumes the argument and stores the parsed value in the variable pointed to by val. An error is returned in case there is no argument or the argument is not a valid base-10, 16-bit integer.
func (*Option) ArgInt32 ¶
ArgInt32 consumes the argument and returns the parsed value. An error is returned in case there is no argument or the argument is not a valid base-10, 32-bit integer.
func (*Option) ArgInt32Var ¶
ArgInt32Var consumes the argument and stores the parsed value in the variable pointed to by val. An error is returned in case there is no argument or the argument is not a valid base-10, 32-bit integer.
func (*Option) ArgInt64 ¶
ArgInt64 consumes the argument and returns the parsed value. An error is returned in case there is no argument or the argument is not a valid base-10, 64-bit integer.
func (*Option) ArgInt64Var ¶
ArgInt64Var consumes the argument and stores the parsed value in the variable pointed to by val. An error is returned in case there is no argument or the argument is not a valid base-10, 64-bit integer.
func (*Option) ArgIntVar ¶
ArgIntVar consumes the argument and stores the parsed value in the variable pointed to by val. An error is returned in case there is no argument or the argument is not a valid base-10 integer.
func (*Option) ArgTextVar ¶
func (opt *Option) ArgTextVar(val encoding.TextUnmarshaler) error
ArgTextVar consumes the argument and calls UnmarshalText on val with the argument as parameter. An error is returned in case there is no argument or UnmarshalText returned an error.
func (*Option) ArgUint ¶
ArgUint consumes the argument and returns the parsed value. An error is returned in case there is no argument or the argument is not a valid unsigned base-10 integer.
func (*Option) ArgUint8 ¶
ArgUint8 consumes the argument and returns the parsed value. An error is returned in case there is no argument or the argument is not a valid unsigned base-10, 8-bit integer.
func (*Option) ArgUint8Var ¶
ArgUint8Var consumes the argument and stores the parsed value in the variable pointed to by val. An error is returned in case there is no argument or the argument is not a valid unsigned base-10, 8-bit integer.
func (*Option) ArgUint16 ¶
ArgUint16 consumes the argument and returns the parsed value. An error is returned in case there is no argument or the argument is not a valid unsigned base-10, 16-bit integer.
func (*Option) ArgUint16Var ¶
ArgUint16Var consumes the argument and stores the parsed value in the variable pointed to by val. An error is returned in case there is no argument or the argument is not a valid unsigned base-10, 16-bit integer.
func (*Option) ArgUint32 ¶
ArgUint32 consumes the argument and returns the parsed value. An error is returned in case there is no argument or the argument is not a valid unsigned base-10, 32-bit integer.
func (*Option) ArgUint32Var ¶
ArgUint32Var consumes the argument and stores the parsed value in the variable pointed to by val. An error is returned in case there is no argument or the argument is not a valid unsigned base-10, 32-bit integer.
func (*Option) ArgUint64 ¶
ArgUint64 consumes the argument and returns the parsed value. An error is returned in case there is no argument or the argument is not a valid unsigned base-10, 64-bit integer.
func (*Option) ArgUint64Var ¶
ArgUint64Var consumes the argument and stores the parsed value in the variable pointed to by val. An error is returned in case there is no argument or the argument is not a valid unsigned base-10, 64-bit integer.
func (*Option) ArgUintVar ¶
ArgUintVar consumes the argument and stores the parsed value in the variable pointed to by val. An error is returned in case there is no argument or the argument is not a valid unsigned base-10 integer.
func (*Option) ArgVar ¶
Arg consumes the argument and stores it in the variable pointed to by val. An error is returned in case there is no argument.
func (*Option) HasConjoinedArg ¶
HasConjoinedArg reports whether the option has a conjoined argument. An argument that is included in the same argument is considered conjoined.
func (*Option) InvalidArgument ¶
InvalidArgument returns an appropriate error message for an underlying error caused by the argument being invalid.
func (*Option) InvalidArgumentStr ¶
InvalidArgumentStr is like InvalidArgument, but taks a message string instead of an error.