Skip to content

Short option handling doesn’t consume value token for combined short flags (e.g. -som foo) #2254

@0x5a17ed

Description

@0x5a17ed

My urfave/cli version is

( v3.6.2 )

Checklist

  • Are you running the latest v3 release? The list of releases is here.
  • Did you check the manual for your release? The v3 manual is here
  • Did you perform a search about this problem? Here's the GitHub guide about searching.

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:

cli/command_parse.go

Lines 205 to 210 in fa7e0b1

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:

cli/command_parse.go

Lines 166 to 172 in fa7e0b1

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'

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/v3relates to / is being considered for v3kind/bugdescribes or fixes a bugstatus/triagemaintainers still need to look into this

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions