Skip to content

Document .SetInterspersed(false) to Allow Non-Option to Stop Argument Processing #180

@jcburley

Description

@jcburley

The canonical command-line processing for wrappers (such as time and xargs) is for the wrapper to parse its options until it sees the name of the "wrappee" (e.g. ls in time ls -l). All remaining arguments on the command line are treated as arguments to that wrappee.

E.g. consider this program:

package main

import (
	"fmt"
	"io"
	"os"

	"github.com/spf13/pflag"
)

var (
	test1Flag = pflag.Bool("t1", false, "test flag one (bool)")
	test2Flag = pflag.Bool("t2", false, "test another flag (bool)")
	test3Flag = pflag.Int("t3", 5, "test a third flag (int)")
)

var stdout io.Writer = os.Stdout
var stderr io.Writer = os.Stderr

func main() {
	pflag.Usage = func() {
		usage := "Usage: %s [<options>] [<command>] [<command-args>] ...\n\n"
		usage += "Flags:\n"
		fmt.Fprintf(stderr, usage, os.Args[0])
		pflag.PrintDefaults()
	}
	
	pflag.Parse()

	fmt.Fprintf(stderr, "t1=%v t2=%v t3=%v\n#args=%v command-with-args=%v\n",
		*test1Flag, *test2Flag, *test3Flag,
		pflag.NArg(), pflag.Args())
}

The following examples show undesirable processing happening (but it's technically correct, in that nothing in the above code tells pflags to stop processing options after seeing the first non-option):

[1] craig@doe:~/.go/src/github.com/jcburley/wrapit$ ./wrapit 
t1=false t2=false t3=5
#args=0 command-with-args=[]
[1] craig@doe:~/.go/src/github.com/jcburley/wrapit$ ./wrapit x a b --t1 --t3 6
t1=true t2=false t3=6
#args=3 command-with-args=[x a b]
[1] craig@doe:~/.go/src/github.com/jcburley/wrapit$ ./wrapit --t2 x --t3 7 a b --t1 --t3 6
t1=true t2=true t3=6
#args=3 command-with-args=[x a b]
[1] craig@doe:~/.go/src/github.com/jcburley/wrapit$

The desired outputs would be:

[1] craig@doe:~/.go/src/github.com/jcburley/wrapit$ ./wrapit  # Same output.
t1=false t2=false t3=5
#args=0 command-with-args=[]
[1] craig@doe:~/.go/src/github.com/jcburley/wrapit$ ./wrapit x a b --t1 --t3 6
t1=false t2=false t3=5
#args=6 command-with-args=[x a b --t1 --t3 6]
[1] craig@doe:~/.go/src/github.com/jcburley/wrapit$ ./wrapit --t2 x --t3 7 a b --t1 --t3 6
t1=false t2=true t3=5
#args=8 command-with-args=[x --t3 7 a b --t1 --t3 6]
[1] craig@doe:~/.go/src/github.com/jcburley/wrapit$

This differs from #160 in the crucial aspect that any flags, beyond the first non-option (x in the examples above), are not considered "unknown" -- they would be not processed at all, so would have no effect on the pertinent variables even if recognized.

E.g. consider the time utility:

[1] craig@doe:~/.go/src/github.com/jcburley/wrapit$ time echo hey
hey

real	0m0.000s
user	0m0.000s
sys	0m0.000s
[1] craig@doe:~/.go/src/github.com/jcburley/wrapit$ time -p echo hey
hey
real 0.00
user 0.00
sys 0.00
[1] craig@doe:~/.go/src/github.com/jcburley/wrapit$ time echo hey -p
hey -p

real	0m0.000s
user	0m0.000s
sys	0m0.000s
[1] craig@doe:~/.go/src/github.com/jcburley/wrapit$

Here, the -p option is not interpreted to have its meaning when it appears after the first non-option (echo), so it is passed, unscathed, to that command.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions