Skip to content

fix: parse single-dash flags whose names start with a digit - #2474

Open
ybai08 wants to merge 1 commit into
urfave:mainfrom
ybai08:fix/digit-flag-names
Open

ybai08 wants to merge 1 commit into
urfave:mainfrom
ybai08:fix/digit-flag-names

Conversation

@ybai08

@ybai08 ybai08 commented Oct 17, 2026

Copy link
Copy Markdown

What type of PR is this?

  • bug

What this PR does / why we need it:

A flag whose name starts with a digit can't be given with a single dash any more. With &BoolFlag{Name: "4"}, running app -4 operand leaves the flag unset, and cmd.Args() comes back as ["-4", "operand"]. Flag parsing also stops at -4, so any flags after it are ignored as well. --4 still works. Digit flags are fairly common in real tools (ssh -4/-6, curl -0, ls -1), and getopt and the POSIX utility syntax guidelines treat -4 as an option when the utility defines one.

#2088 (released in v3.2.0) added the cause: any single-dash argument whose first character isn't a letter ends flag parsing, so that negative numbers such as -10 reach positional arguments. That check comes before flag lookup, so it also catches flags that really are defined. Before that, v3.1.1 and v2 (both on the standard flag package) parsed -4 as the flag.

  • command_parse.go: stop parsing at a single-dash argument that doesn't start with a letter only when it doesn't name a defined flag. A new helper, startsWithAppliedFlag, checks the name before any =. With UseShortOptionHandling it also checks the group's first character, so -46 sets both 4 and 6. Undefined arguments like -5 still become positional, so negative-number arguments behave as before.
  • command_test.go: add TestCommand_DigitFlag. It covers -4, -4 -6 -level high, a name that starts with a digit (-2fa=on), a short option group (-46), and a control case showing that an undefined -5 still ends flag parsing.

Which issue(s) this PR fixes:

None filed. I found this on current main while comparing single-dash parsing with v3.1. I didn't find a matching issue or a competing PR.

Special notes for your reviewer:

If a flag named 1 exists, -1 now always means that flag and is never a negative-number positional. getopt and v3.1 behave the same way, and only commands that define such a flag are affected.

Testing

  • go test -run TestCommand_DigitFlag .: the first 4 cases fail on main (192ed0b) and pass with this change. The control case passes on both.
  • Reproduced the old behaviour with a scratch test: on v3.1.1 -4 and -4v set the flags, and on v3.2.0 both are left as positional arguments. On v2-maint -4 sets the flag.
  • macOS (arm64), Go 1.27.1: go test ./..., make generate, make vet, make test, make test GFLAGS='--tags urfave_cli_no_template', make check-binary-size, make diffcheck, make v3diff, gofmt -l ., goimports -l . and golangci-lint run (v2 latest) pass. Generated API documentation is unchanged.
  • Not run: make gfmrun (gfmrun not installed) and Windows/Linux CI.

Release Notes

Single-dash flags whose names start with a digit, such as `-4`, are parsed as flags again instead of ending flag parsing.

Since v3.2.0 an argument such as "-4" is treated as the first positional
argument, and flag parsing stops there, whenever it does not start with
a letter. That keeps negative numbers out of flag parsing, but it also
means a flag named "4" can no longer be given as "-4", which worked in
v3.1 and v2.

Only stop at such an argument when it does not name a defined flag.
@ybai08
ybai08 requested a review from a team as a code owner October 17, 2026 14:31

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant