package main import ( "fmt" "os" "strconv" "strings" ) // Options contains the arguments understood by this small example CLI. type Options struct { Name string Count int Verbose bool Help bool Arguments []string } // ParseArgs parses long options, short options, and positional arguments. func ParseArgs(args []string) (Options, error) { opts := Options{Count: 1} for len(args) > 0 { arg := args[0] args = args[1:] switch { case arg == "--": opts.Arguments = append(opts.Arguments, args...) return opts, nil case arg == "-h" || arg == "--help": opts.Help = true case arg == "-v" || arg == "--verbose": opts.Verbose = true case arg == "-n" || arg == "--name": if len(args) == 0 { return opts, fmt.Errorf("%s requires a value", arg) } opts.Name, args = args[0], args[1:] case strings.HasPrefix(arg, "--name="): opts.Name = strings.TrimPrefix(arg, "--name=") case arg == "-c" || arg == "--count": if len(args) == 0 { return opts, fmt.Errorf("%s requires a value", arg) } var err error opts.Count, err = positiveInt(args[0]) if err != nil { return opts, fmt.Errorf("%s: %w", arg, err) } args = args[1:] case strings.HasPrefix(arg, "--count="): var err error opts.Count, err = positiveInt(strings.TrimPrefix(arg, "--count=")) if err != nil { return opts, fmt.Errorf("--count: %w", err) } case strings.HasPrefix(arg, "-"): return opts, fmt.Errorf("unknown option %q", arg) default: opts.Arguments = append(opts.Arguments, arg) } } return opts, nil } func positiveInt(value string) (int, error) { n, err := strconv.Atoi(value) if err != nil || n < 1 { return 0, fmt.Errorf("expected a positive integer, got %q", value) } return n, nil } func usage() { fmt.Fprintf(os.Stderr, `Usage: %s [options] [arguments...] Options: -n, --name NAME name used in the greeting -c, --count N number of greetings (default 1) -v, --verbose print additional information -h, --help show this help `, os.Args[0]) } func main() { opts, err := ParseArgs(os.Args[1:]) if err != nil { fmt.Fprintln(os.Stderr, "error:", err) usage() os.Exit(2) } if opts.Help { usage() return } name := opts.Name if name == "" { name = "world" } if opts.Verbose { fmt.Fprintf(os.Stderr, "name=%q count=%d arguments=%q\n", name, opts.Count, opts.Arguments) } for i := 0; i < opts.Count; i++ { fmt.Printf("Hello, %s!\n", name) } }