My urfave/cli version is
( v3.6.2 )
Checklist
Dependency Management
- My project is using go modules.
Describe the bug
Short option handling doesn’t consume value token for combined short flags but they are consumed if short flags appear individually. (e.g. foo in -som foo baa will be parsed twice)
To reproduce
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/urfave/cli/v3"
)
func main() {
cmd := &cli.Command{
UseShortOptionHandling: true,
Commands: []*cli.Command{
{
Name: "short",
Usage: "complete a task on the list",
Arguments: []cli.Argument{
&cli.StringArg{Name: "root", UsageText: "Root path"},
},
Flags: []cli.Flag{
&cli.BoolFlag{Name: "serve", Aliases: []string{"s"}},
&cli.BoolFlag{Name: "option", Aliases: []string{"o"}},
&cli.StringFlag{Name: "message", Aliases: []string{"m"}},
},
Action: func(ctx context.Context, cmd *cli.Command) error {
fmt.Println("serve:", cmd.Bool("serve"))
fmt.Println("option:", cmd.Bool("option"))
fmt.Println("message:", cmd.String("message"))
fmt.Println("root:", cmd.StringArg("root"))
return nil
},
},
},
}
if err := cmd.Run(context.Background(), os.Args); err != nil {
log.Fatal(err)
}
}
Observed behavior
$ go run ./cmd/uclitest short -som foo baa
serve: true
option: true
message: foo
root: foo
Expected behavior
Compressed arguments like this:
$ go run ./cmd/uclitest short -som foo baa
serve: true
option: true
message: foo
root: baa
Should show the same behavior as uncompressed form:
$ go run ./cmd/uclitest short -so -m foo baa
serve: true
option: true
message: foo
root: baa
Additional context
-- None --
Want to fix this yourself?
We'd love to have more contributors on this project! If the fix for
this bug is easily explained and very small, feel free to create a
pull request for it.
Sure can do. Looks like the fix would be here:
|
if flagVal == "" { |
|
if len(rargs) == 1 { |
|
return &stringSliceArgs{posArgs}, fmt.Errorf("%s%s", argumentNotProvidedErrMsg, string(c)) |
|
} |
|
flagVal = rargs[1] |
|
} |
this is missing a single
if flagVal == "" {
if len(rargs) == 1 {
return &stringSliceArgs{posArgs}, fmt.Errorf("%s%s", argumentNotProvidedErrMsg, string(c))
}
flagVal = rargs[1]
+ rargs = rargs[1:]
}
just like how it's already handled here:
|
if flagVal == "" { |
|
if len(rargs) == 1 || valFromEqual { |
|
return &stringSliceArgs{posArgs}, fmt.Errorf("%s%s", argumentNotProvidedErrMsg, firstArg) |
|
} |
|
flagVal = rargs[1] |
|
rargs = rargs[1:] |
|
} |
Run go version and paste its output here
# paste `go version` output in here
$ go version
go version go1.25.6 X:nodwarf5 linux/amd64
Run go env and paste its output here
# paste `go env` output in here
$ go env
AR='ar'
CC='gcc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='g++'
GCCGO='gccgo'
GO111MODULE=''
GOAMD64='v1'
GOARCH='amd64'
GOAUTH='netrc'
GOBIN=''
GOCACHE='redacted/.cache/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='redacted/.config/go/env'
GOEXE=''
GOEXPERIMENT='nodwarf5'
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build2350388969=/tmp/go-build -gno-record-gcc-switches'
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMOD='redacted/go.mod'
GOMODCACHE='redacted/.local/share/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='redacted/.local/share/go'
GOPRIVATE=''
GOPROXY='direct'
GOROOT='/usr/lib/go'
GOSUMDB='off'
GOTELEMETRY='local'
GOTELEMETRYDIR='redacted/.config/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='local'
GOTOOLDIR='/usr/lib/go/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.25.6 X:nodwarf5'
GOWORK=''
PKG_CONFIG='pkg-config'
My urfave/cli version is
( v3.6.2 )
Checklist
Dependency Management
Describe the bug
Short option handling doesn’t consume value token for combined short flags but they are consumed if short flags appear individually. (e.g. foo in
-som foo baawill be parsed twice)To reproduce
Observed behavior
Expected behavior
Compressed arguments like this:
Should show the same behavior as uncompressed form:
Additional context
-- None --
Want to fix this yourself?
Sure can do. Looks like the fix would be here:
cli/command_parse.go
Lines 205 to 210 in fa7e0b1
this is missing a single
if flagVal == "" { if len(rargs) == 1 { return &stringSliceArgs{posArgs}, fmt.Errorf("%s%s", argumentNotProvidedErrMsg, string(c)) } flagVal = rargs[1] + rargs = rargs[1:] }just like how it's already handled here:
cli/command_parse.go
Lines 166 to 172 in fa7e0b1
Run
go versionand paste its output hereRun
go envand paste its output here