getopt

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 2, 2026 License: ISC Imports: 5 Imported by: 0

README

GitLab License Go Reference

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, this implementation does not rely on global state, and the error interface is used throughout for all error cases.

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.

If that sounds interesting to you, check out the docs at pkg.go.dev. They include an example and a much more detailed description of the functionality.

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

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Getopt

func Getopt(args []string, fn func(*Option) error) ([]string, error)

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

func (opt *Option) Arg() (string, error)

Arg consumes the argument and returns it. An error is returned in case there is no argument.

func (*Option) ArgFloat32

func (opt *Option) ArgFloat32() (float32, error)

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

func (opt *Option) ArgFloat32Var(val *float32) error

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

func (opt *Option) ArgFloat64() (float64, error)

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

func (opt *Option) ArgFloat64Var(val *float64) error

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

func (opt *Option) ArgInt() (int, error)

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

func (opt *Option) ArgInt8() (int8, error)

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

func (opt *Option) ArgInt8Var(val *int8) error

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

func (opt *Option) ArgInt16() (int16, error)

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

func (opt *Option) ArgInt16Var(val *int16) error

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

func (opt *Option) ArgInt32() (int32, error)

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

func (opt *Option) ArgInt32Var(val *int32) error

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

func (opt *Option) ArgInt64() (int64, error)

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

func (opt *Option) ArgInt64Var(val *int64) error

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

func (opt *Option) ArgIntVar(val *int) error

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

func (opt *Option) ArgUint() (uint, error)

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

func (opt *Option) ArgUint8() (uint8, error)

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

func (opt *Option) ArgUint8Var(val *uint8) error

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

func (opt *Option) ArgUint16() (uint16, error)

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

func (opt *Option) ArgUint16Var(val *uint16) error

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

func (opt *Option) ArgUint32() (uint32, error)

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

func (opt *Option) ArgUint32Var(val *uint32) error

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

func (opt *Option) ArgUint64() (uint64, error)

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

func (opt *Option) ArgUint64Var(val *uint64) error

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

func (opt *Option) ArgUintVar(val *uint) error

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

func (opt *Option) ArgVar(val *string) error

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

func (opt *Option) HasConjoinedArg() bool

HasConjoinedArg reports whether the option has a conjoined argument. An argument that is included in the same argument is considered conjoined.

func (*Option) InvalidArgument

func (opt *Option) InvalidArgument(err error) error

InvalidArgument returns an appropriate error message for an underlying error caused by the argument being invalid.

func (*Option) InvalidArgumentStr

func (opt *Option) InvalidArgumentStr(msg string) error

InvalidArgumentStr is like InvalidArgument, but taks a message string instead of an error.

func (*Option) Name

func (opt *Option) Name() string

Name returns the normalized name of the option. Short options start with a single dash, long options with two dashes. e.g. the b in -abc will be reported as "-b".

func (*Option) Unknown

func (opt *Option) Unknown() error

Unknown returns an appropriate error message for an option that is not supported by the program.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL