From 582e6d537a34c8d16bbb401b70f590d5502bbd73 Mon Sep 17 00:00:00 2001 From: guoguangwu Date: Wed, 15 Nov 2023 17:58:55 +0800 Subject: fix: typo Signed-off-by: guoguangwu --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'README.md') diff --git a/README.md b/README.md index f105b17..7b3d148 100644 --- a/README.md +++ b/README.md @@ -591,7 +591,7 @@ https://godoc.org/github.com/alexflint/go-arg There are many command line argument parsing libraries for Go, including one in the standard library, so why build another? -The `flag` library that ships in the standard library seems awkward to me. Positional arguments must preceed options, so `./prog x --foo=1` does what you expect but `./prog --foo=1 x` does not. It also does not allow arguments to have both long (`--foo`) and short (`-f`) forms. +The `flag` library that ships in the standard library seems awkward to me. Positional arguments must precede options, so `./prog x --foo=1` does what you expect but `./prog --foo=1 x` does not. It also does not allow arguments to have both long (`--foo`) and short (`-f`) forms. Many third-party argument parsing libraries are great for writing sophisticated command line interfaces, but feel to me like overkill for a simple script with a few flags. -- cgit v1.2.3 From 84ddf1d244f4bbe299f082e1c880c34831f49c57 Mon Sep 17 00:00:00 2001 From: James Shubin Date: Wed, 28 Feb 2024 22:29:16 -0500 Subject: add an example for environment vars with arg names If you want to specify both of these, and if they should have different names, then this shows you how it can be done. --- README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'README.md') diff --git a/README.md b/README.md index f105b17..3ea28cc 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,23 @@ $ WORKERS='1,99' ./example Workers: [1 99] ``` +You can also have an environment variable that doesn't match the arg name: + +```go +var args struct { + Workers int `arg:"--count,env:NUM_WORKERS"` +} +arg.MustParse(&args) +fmt.Println("Workers:", args.Workers) +``` + +``` +$ NUM_WORKERS=6 ./example +Workers: 6 +$ NUM_WORKERS=6 ./example --count 4 +Workers: 4 +``` + ### Usage strings ```go var args struct { -- cgit v1.2.3 From c087d7180231ea3cfc12a79ee091786ac9954e6a Mon Sep 17 00:00:00 2001 From: Hugo Hromic Date: Sat, 15 Jul 2023 15:12:58 +0100 Subject: Add note for version flag overriding to README --- README.md | 3 +++ 1 file changed, 3 insertions(+) (limited to 'README.md') diff --git a/README.md b/README.md index f105b17..c644227 100644 --- a/README.md +++ b/README.md @@ -284,6 +284,9 @@ $ ./example --version someprogram 4.3.0 ``` +> **Note** +> If a `--version` flag is defined in `args` or any subcommand, it overrides the built-in versioning. + ### Overriding option names ```go -- cgit v1.2.3 From 7fd624cf1c8273b4891562f4903c6bbb3243e32b Mon Sep 17 00:00:00 2001 From: Alex Flint Date: Wed, 4 Sep 2024 10:27:34 -0400 Subject: add info to README about programmatically reproducing behavior of MustParse --- README.md | 244 ++++++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 200 insertions(+), 44 deletions(-) (limited to 'README.md') diff --git a/README.md b/README.md index 4d23034..9b8b071 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ $ NUM_WORKERS=4 ./example Workers: 4 ``` -You can provide multiple values using the CSV (RFC 4180) format: +You can provide multiple values in environment variables using commas: ```go var args struct { @@ -120,7 +120,7 @@ $ WORKERS='1,99' ./example Workers: [1 99] ``` -You can also have an environment variable that doesn't match the arg name: +Command line arguments take precedence over environment variables: ```go var args struct { @@ -175,20 +175,7 @@ var args struct { arg.MustParse(&args) ``` -### Default values (before v1.2) - -```go -var args struct { - Foo string - Bar bool -} -arg.Foo = "abc" -arg.MustParse(&args) -``` - -### Combining command line options, environment variables, and default values - -You can combine command line arguments, environment variables, and default values. Command line arguments take precedence over environment variables, which take precedence over default values. This means that we check whether a certain option was provided on the command line, then if not, we check for an environment variable (only if an `env` tag was provided), then if none is found, we check for a `default` tag containing a default value. +Command line arguments take precedence over environment variables, which take precedence over default values. This means that we check whether a certain option was provided on the command line, then if not, we check for an environment variable (only if an `env` tag was provided), then if none is found, we check for a `default` tag containing a default value. ```go var args struct { @@ -198,10 +185,6 @@ arg.MustParse(&args) ``` #### Ignoring environment variables and/or default values - -The values in an existing structure can be kept in-tact by ignoring environment -variables and/or default values. - ```go var args struct { Test string `arg:"-t,env:TEST" default:"something"` @@ -261,26 +244,7 @@ fmt.Println(args.UserIDs) map[john:123 mary:456] ``` -### Custom validation -```go -var args struct { - Foo string - Bar string -} -p := arg.MustParse(&args) -if args.Foo == "" && args.Bar == "" { - p.Fail("you must provide either --foo or --bar") -} -``` - -```shell -./example -Usage: samples [--foo FOO] [--bar BAR] -error: you must provide either --foo or --bar -``` - ### Version strings - ```go type args struct { ... @@ -304,6 +268,24 @@ someprogram 4.3.0 > **Note** > If a `--version` flag is defined in `args` or any subcommand, it overrides the built-in versioning. +### Custom validation +```go +var args struct { + Foo string + Bar string +} +p := arg.MustParse(&args) +if args.Foo == "" && args.Bar == "" { + p.Fail("you must provide either --foo or --bar") +} +``` + +```shell +./example +Usage: samples [--foo FOO] [--bar BAR] +error: you must provide either --foo or --bar +``` + ### Overriding option names ```go @@ -452,8 +434,6 @@ main.NameDotName{Head:"file", Tail:"txt"} ### Custom placeholders -*Introduced in version 1.3.0* - Use the `placeholder` tag to control which placeholder text is used in the usage text. ```go @@ -541,8 +521,6 @@ For more information visit github.com/alexflint/go-arg ### Subcommands -*Introduced in version 1.1.0* - Subcommands are commonly used in tools that wish to group multiple functions into a single program. An example is the `git` tool: ```shell $ git checkout [arguments specific to checking out code] @@ -603,6 +581,184 @@ if p.Subcommand() == nil { } ``` + +### Programmatic error handling + +The following reproduces the internal logic of `MustParse` for the simple case where +you are not using subcommands or --version. This allows you to respond +programatically to --help, and to any errors that come up. + +```go +var args struct { + Something string +} + +p, err := arg.NewParser(arg.Config{}, &args) +if err != nil { + log.Fatalf("there was an error in the definition of the Go struct: %v", err) +} + +err = p.Parse(os.Args[1:]) +switch { +case err == arg.ErrHelp: // indicates that user wrote "--help" on command line + p.WriteHelp(os.Stdout) + os.Exit(0) +case err != nil: + fmt.Printf("error: %v\n", err) + p.WriteUsage(os.Stdout) + os.Exit(1) +} +``` + +```shell +$ go run ./example --help +Usage: ./example --something SOMETHING + +Options: + --something SOMETHING + --help, -h display this help and exit + +$ ./example --wrong +error: unknown argument --wrong +Usage: ./example --something SOMETHING + +$ ./example +error: --something is required +Usage: ./example --something SOMETHING + +$ ./example --something abc +got "abc" +``` + +To also handle --version programatically, use the following: + +```go +type args struct { + Something string +} + +func (args) Version() string { + return "1.2.3" +} + +func main() { + var args args + p, err := arg.NewParser(arg.Config{}, &args) + if err != nil { + log.Fatalf("there was an error in the definition of the Go struct: %v", err) + } + + err = p.Parse(os.Args[1:]) + switch { + case err == arg.ErrHelp: // found "--help" on command line + p.WriteHelp(os.Stdout) + os.Exit(0) + case err == arg.ErrVersion: // found "--version" on command line + fmt.Println(args.Version()) + os.Exit(0) + case err != nil: + fmt.Printf("error: %v\n", err) + p.WriteUsage(os.Stdout) + os.Exit(1) + } + + fmt.Printf("got %q\n", args.Something) +} +``` + +```shell +$ ./example --version +1.2.3 + +$ go run ./example --help +1.2.3 +Usage: example --something SOMETHING + +Options: + --something SOMETHING + --help, -h display this help and exit + +$ ./example --wrong +1.2.3 +error: unknown argument --wrong +Usage: example --something SOMETHING + +$ ./example +error: --something is required +Usage: example --something SOMETHING + +$ ./example --something abc +got "abc" +``` + +To also handle subcommands, use this most general version (also works in absence of subcommands but +is a bit more complex): + +```go +type fetchCmd struct { + Count int +} + +type args struct { + Something string + Fetch *fetchCmd `arg:"subcommand"` +} + +func (args) Version() string { + return "1.2.3" +} + +func main() { + var args args + p, err := arg.NewParser(arg.Config{}, &args) + if err != nil { + log.Fatalf("there was an error in the definition of the Go struct: %v", err) + } + + err = p.Parse(os.Args[1:]) + switch { + case err == arg.ErrHelp: // found "--help" on command line + p.WriteHelpForSubcommand(os.Stdout, p.SubcommandNames()...) + os.Exit(0) + case err == arg.ErrVersion: // found "--version" on command line + fmt.Println(args.Version()) + os.Exit(0) + case err != nil: + fmt.Printf("error: %v\n", err) + p.WriteUsageForSubcommand(os.Stdout, p.SubcommandNames()...) + os.Exit(1) + } +}``` + +```shell +$ ./example --version +1.2.3 + +$ ./example --help +1.2.3 +Usage: example [--something SOMETHING] [] + +Options: + --something SOMETHING + --help, -h display this help and exit + --version display version and exit + +Commands: + fetch + +$ ./example fetch --help +1.2.3 +Usage: example fetch [--count COUNT] + +Options: + --count COUNT + +Global options: + --something SOMETHING + --help, -h display this help and exit + --version display version and exit +``` + ### API Documentation https://godoc.org/github.com/alexflint/go-arg @@ -619,4 +775,4 @@ The idea behind `go-arg` is that Go already has an excellent way to describe dat ### Backward compatibility notes -Earlier versions of this library required the help text to be part of the `arg` tag. This is still supported but is now deprecated. Instead, you should use a separate `help` tag, described above, which removes most of the limits on the text you can write. In particular, you will need to use the new `help` tag if your help text includes any commas. +Earlier versions of this library required the help text to be part of the `arg` tag. This is still supported but is now deprecated. Instead, you should use a separate `help` tag, described above, which makes it possible to include commas inside help text. -- cgit v1.2.3 From b13a62172a12a2b2f0cfd7eeed10d846845a5f77 Mon Sep 17 00:00:00 2001 From: Alex Flint Date: Thu, 5 Sep 2024 17:15:02 -0400 Subject: update api docs for Parser.Parse --- README.md | 14 ++++---------- parse.go | 9 ++++++++- 2 files changed, 12 insertions(+), 11 deletions(-) (limited to 'README.md') diff --git a/README.md b/README.md index 9b8b071..761af56 100644 --- a/README.md +++ b/README.md @@ -582,7 +582,7 @@ if p.Subcommand() == nil { ``` -### Programmatic error handling +### Custom handling of --help and --version The following reproduces the internal logic of `MustParse` for the simple case where you are not using subcommands or --version. This allows you to respond @@ -625,9 +625,6 @@ Usage: ./example --something SOMETHING $ ./example error: --something is required Usage: ./example --something SOMETHING - -$ ./example --something abc -got "abc" ``` To also handle --version programatically, use the following: @@ -686,13 +683,10 @@ Usage: example --something SOMETHING $ ./example error: --something is required Usage: example --something SOMETHING - -$ ./example --something abc -got "abc" ``` -To also handle subcommands, use this most general version (also works in absence of subcommands but -is a bit more complex): +To generate subcommand-specific help messages, use the following most general version +(this also works in absence of subcommands but is a bit more complex): ```go type fetchCmd struct { @@ -761,7 +755,7 @@ Global options: ### API Documentation -https://godoc.org/github.com/alexflint/go-arg +https://pkg.go.dev/github.com/alexflint/go-arg ### Rationale diff --git a/parse.go b/parse.go index 2bed8bf..8f99a21 100644 --- a/parse.go +++ b/parse.go @@ -494,7 +494,14 @@ func cmdFromStruct(name string, dest path, t reflect.Type) (*command, error) { } // Parse processes the given command line option, storing the results in the field -// of the structs from which NewParser was constructed +// of the structs from which NewParser was constructed. +// +// It returns ErrHelp if "--help" is one of the command line args and ErrVersion if +// "--version" is one of the command line args (the latter only applies if the +// destination struct passed to NewParser implements Versioned.) +// +// To respond to --help and --version in the way that MustParse does, see examples +// in the README under "Custom handling of --help and --version". func (p *Parser) Parse(args []string) error { err := p.process(args) if err != nil { -- cgit v1.2.3 From cb7e5c190570d24d0768224f08a11ce8bf607b41 Mon Sep 17 00:00:00 2001 From: Hugo Hromic Date: Mon, 1 Jul 2024 23:16:51 +0100 Subject: Add global env prefix example to README * Also made newline separations around sections consistent * Also fixed usage of `p.Parse()` in env variable ignore example --- README.md | 50 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 11 deletions(-) (limited to 'README.md') diff --git a/README.md b/README.md index 761af56..e9075ba 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ fmt.Println("Input:", args.Input) fmt.Println("Output:", args.Output) ``` -``` +```shell $ ./example src.txt x.out y.out z.out Input: src.txt Output: [x.out y.out z.out] @@ -80,12 +80,12 @@ arg.MustParse(&args) fmt.Println("Workers:", args.Workers) ``` -``` +```shell $ WORKERS=4 ./example Workers: 4 ``` -``` +```shell $ WORKERS=4 ./example --workers=6 Workers: 6 ``` @@ -100,7 +100,7 @@ arg.MustParse(&args) fmt.Println("Workers:", args.Workers) ``` -``` +```shell $ NUM_WORKERS=4 ./example Workers: 4 ``` @@ -115,7 +115,7 @@ arg.MustParse(&args) fmt.Println("Workers:", args.Workers) ``` -``` +```shell $ WORKERS='1,99' ./example Workers: [1 99] ``` @@ -130,14 +130,35 @@ arg.MustParse(&args) fmt.Println("Workers:", args.Workers) ``` -``` +```shell $ NUM_WORKERS=6 ./example Workers: 6 $ NUM_WORKERS=6 ./example --count 4 Workers: 4 ``` +Configuring a global environment variable name prefix is also possible: + +```go +var args struct { + Workers int `arg:"--count,env:NUM_WORKERS"` +} + +p, err := arg.NewParser(arg.Config{ + EnvPrefix: "MYAPP_", +}, &args) + +p.MustParse(os.Args[1:]) +fmt.Println("Workers:", args.Workers) +``` + +```shell +$ MYAPP_NUM_WORKERS=6 ./example +Workers: 6 +``` + ### Usage strings + ```go var args struct { Input string `arg:"positional"` @@ -185,6 +206,7 @@ arg.MustParse(&args) ``` #### Ignoring environment variables and/or default values + ```go var args struct { Test string `arg:"-t,env:TEST" default:"something"` @@ -195,10 +217,11 @@ p, err := arg.NewParser(arg.Config{ IgnoreDefault: true, }, &args) -err = p.Parse(os.Args) +err = p.Parse(os.Args[1:]) ``` ### Arguments with multiple values + ```go var args struct { Database string @@ -214,6 +237,7 @@ Fetching the following IDs from foo: [1 2 3] ``` ### Arguments that can be specified multiple times, mixed with positionals + ```go var args struct { Commands []string `arg:"-c,separate"` @@ -231,6 +255,7 @@ Databases [db1 db2 db3] ``` ### Arguments with keys and values + ```go var args struct { UserIDs map[string]int @@ -245,6 +270,7 @@ map[john:123 mary:456] ``` ### Version strings + ```go type args struct { ... @@ -269,6 +295,7 @@ someprogram 4.3.0 > If a `--version` flag is defined in `args` or any subcommand, it overrides the built-in versioning. ### Custom validation + ```go var args struct { Foo string @@ -310,13 +337,11 @@ Options: --help, -h display this help and exit ``` - ### Embedded structs The fields of embedded structs are treated just like regular fields: ```go - type DatabaseOptions struct { Host string Username string @@ -384,6 +409,7 @@ func main() { fmt.Printf("%#v\n", args.Name) } ``` + ```shell $ ./example --name=foo.bar main.NameDotName{Head:"foo", Tail:"bar"} @@ -420,6 +446,7 @@ func main() { fmt.Printf("%#v\n", args.Name) } ``` + ```shell $ ./example --help Usage: test [--name NAME] @@ -445,6 +472,7 @@ var args struct { } arg.MustParse(&args) ``` + ```shell $ ./example -h Usage: example [--optimize LEVEL] [--maxjobs N] SRC [DST [DST ...]] @@ -581,7 +609,6 @@ if p.Subcommand() == nil { } ``` - ### Custom handling of --help and --version The following reproduces the internal logic of `MustParse` for the simple case where @@ -722,7 +749,8 @@ func main() { p.WriteUsageForSubcommand(os.Stdout, p.SubcommandNames()...) os.Exit(1) } -}``` +} +``` ```shell $ ./example --version -- cgit v1.2.3