A Go implementation of POSIX getopt(3), GNU getopt_long(3), and getopt_long_only(3) with native subcommand support.
OptArgs Core is the foundation for API-compatible wrapper modules that serve as drop-in replacements for popular Go argument parsing libraries.
OptArgs is a general-purpose, unopinionated parser that faithfully implements GNU/POSIX getopt(3), getopt_long(3), and getopt_long_only(3) behavior. It exposes the full API surface needed to construct opinionated parsers on top — goarg and pflag are two such compatibility layers, each matching the API conventions of their upstream counterparts while gaining the correctness and features of the core engine.
- Full POSIX getopt(3) compliance: short options, compaction,
--termination,POSIXLY_CORRECT - GNU getopt_long(3):
--option=value,--option value, abbreviation matching, case-insensitive - GNU getopt_long_only(3): single-dash long options with short option fallback
- Advanced handling:
-Wextension, option redefinition, negative arguments - Native subcommand dispatch via
AddCmd()with option inheritance through the parent chain - Iterator-based processing (
Options()returnsiter.Seq2[Option, error]) - Verbose and silent error modes, both working through subcommand chains
- Zero dependencies
| Module | Upstream | Description |
|---|---|---|
| goarg | alexflint/go-arg | API-compatible drop-in replacement using struct tags |
| pflag | spf13/pflag | API-compatible drop-in replacement using flag methods |
go get github.com/major0/optargsRequires Go 1.23+.
package main
import (
"fmt"
"github.com/major0/optargs"
)
func main() {
p, _ := optargs.GetOpt(os.Args[1:], "vf:o::")
for opt, err := range p.Options() {
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
continue
}
switch opt.Name {
case "v":
fmt.Println("verbose")
case "f":
fmt.Println("file:", opt.Arg)
case "o":
fmt.Println("output:", opt.Arg)
}
}
}p, _ := optargs.GetOptLong(os.Args[1:], "vf:", []optargs.Flag{
{Name: "verbose", HasArg: optargs.NoArgument},
{Name: "file", HasArg: optargs.RequiredArgument},
{Name: "output", HasArg: optargs.OptionalArgument},
})
for opt, err := range p.Options() {
// ...
}p, _ := optargs.GetOptLongOnly(os.Args[1:], "vf:", []optargs.Flag{
{Name: "verbose", HasArg: optargs.NoArgument},
{Name: "file", HasArg: optargs.RequiredArgument},
})
// -verbose tries long match first, falls back to short options via optstringroot, _ := optargs.GetOptLong(os.Args[1:], "v", []optargs.Flag{
{Name: "verbose", HasArg: optargs.NoArgument},
})
serve, _ := optargs.GetOptLong([]string{}, "p:", []optargs.Flag{
{Name: "port", HasArg: optargs.RequiredArgument},
})
root.AddCmd("serve", serve)
// Root iteration dispatches to child when "serve" is encountered.
// Child inherits parent options via parent-chain walk.
for opt, err := range root.Options() { /* root options */ }
for opt, err := range serve.Options() { /* serve options + inherited */ }By default, child parsers inherit parent options — unknown options in a subcommand are resolved by walking the parent chain. To disable this (POSIX-strict behavior where each command owns its own options):
root.SetStrictSubcommands(true)
root.AddCmd("serve", serve) // serve will NOT inherit root's optionsThis is automatically enabled when POSIXLY_CORRECT is set or when the
optstring starts with +.
| Prefix | Behavior |
|---|---|
: |
Silent error mode — suppress error logging |
+ |
POSIXLY_CORRECT — stop at first non-option |
- |
Treat non-options as argument to option \x01 |
| Suffix | Meaning |
|---|---|
f |
No argument |
f: |
Required argument |
f:: |
Optional argument |
W; |
GNU -W word extension |
example/— vanilla GetOpt, GetOptLong, GetOptLongOnly usageposix/— obscure POSIX/GNU patterns: subcommand dispatch, silent error mode, POSIXLY_CORRECT
See CONTRIBUTING.md.