diff --git a/command.go b/command.go index 2cc18891..03a65782 100644 --- a/command.go +++ b/command.go @@ -585,11 +585,15 @@ Loop: case strings.HasPrefix(s, "-") && !strings.Contains(s, "=") && len(s) == 2 && !shortHasNoOptDefVal(s[1:], flags): // If '-f arg' then // delete 'arg' from args or break the loop if len(args) <= 1. - if len(args) <= 1 { + if len(args) == 0 { break Loop - } else { + } + // When the flag 's' doesn't contain "=" and without "no option default values", but + // following with a string starts with '-', consider 's' an unknown boolean flag + // and don't delete 'arg'. + // Otherwise, consider it as '--flag arg' or '-f arg' and delete its following arg. + if !strings.HasPrefix(args[0], "-") { args = args[1:] - continue } case s != "" && !strings.HasPrefix(s, "-"): commands = append(commands, s) diff --git a/command_test.go b/command_test.go index ca90e67c..f203c976 100644 --- a/command_test.go +++ b/command_test.go @@ -2058,3 +2058,94 @@ func TestFParseErrWhitelistSiblingCommand(t *testing.T) { } checkStringContains(t, output, "unknown flag: --unknown") } + +func TestFindSubCommand(t *testing.T) { + root := &Command{ + Use: "root", + Run: emptyRun, + } + type testOpts struct { + a bool + b bool + } + var opt testOpts + child := &Command{ + Use: "child", + RunE: func(cmd *Command, args []string) error { + fmt.Printf("Done! a=%v b=%v\n", opt.a, opt.b) + return nil + }, + } + child.Flags().BoolVar(&opt.a, "a", false, "a") + child.Flags().BoolVar(&opt.b, "b", false, "b") + root.AddCommand(child) + + argsToTest := [][]string{ + {"child", "--a"}, + {"child", "--b"}, + } + for _, args := range argsToTest { + cmdFound, _, err := root.Find(args) + if err != nil { + t.Errorf("Unexpected error: %v with args: %v", err, args) + } + if cmdFound.Name() != "child" { + t.Errorf("expected found 'child' command with args: %v", args) + } + } + + argsToTestNotFound := [][]string{ + {"--a", "--b", "child"}, + {"--a", "child"}, + {"--b", "child"}, + } + for _, args := range argsToTestNotFound { + cmdFound, _, err := root.Find(args) + if err != nil { + t.Errorf("Unexpected error: %v with args: %v", err, args) + } + if cmdFound.Name() != "root" { + t.Errorf("expected found 'root' command with args: %v", args) + } + } +} + +func TestFParseUnknownFlagFromSubCommand(t *testing.T) { + root := &Command{ + Use: "root", + Run: emptyRun, + } + type testOpts struct { + a bool + b bool + } + var opt testOpts + child := &Command{ + Use: "child", + RunE: func(cmd *Command, args []string) error { + fmt.Printf("Done! a=%v b=%v\n", opt.a, opt.b) + return nil + }, + } + child.Flags().BoolVar(&opt.a, "a", false, "a") + child.Flags().BoolVar(&opt.b, "b", false, "b") + root.AddCommand(child) + + output, err := executeCommand(root, "--a", "child") + if err == nil { + t.Error("expected unknown flag error") + } + checkStringContains(t, output, "unknown flag: --a") + + output, err = executeCommand(root, "--b", "child") + if err == nil { + t.Error("expected unknown flag error") + } + checkStringContains(t, output, "unknown flag: --b") + + output, err = executeCommand(root, "--a", "--b", "child") + if err == nil { + t.Error("expected unknown flag error") + } + checkStringContains(t, output, "unknown flag: --a") +}