Git Product home page Git Product logo

pflag's Introduction

Build Status Go Report Card GoDoc

Description

pflag is a drop-in replacement for Go's flag package, implementing POSIX/GNU-style --flags.

pflag is compatible with the GNU extensions to the POSIX recommendations for command-line options. For a more precise description, see the "Command-line flag syntax" section below.

pflag is available under the same style of BSD license as the Go language, which can be found in the LICENSE file.

Installation

pflag is available using the standard go get command.

Install by running:

go get github.com/spf13/pflag

Run tests by running:

go test github.com/spf13/pflag

Usage

pflag is a drop-in replacement of Go's native flag package. If you import pflag under the name "flag" then all code should continue to function with no changes.

import flag "github.com/spf13/pflag"

There is one exception to this: if you directly instantiate the Flag struct there is one more field "Shorthand" that you will need to set. Most code never instantiates this struct directly, and instead uses functions such as String(), BoolVar(), and Var(), and is therefore unaffected.

Define flags using flag.String(), Bool(), Int(), etc.

This declares an integer flag, -flagname, stored in the pointer ip, with type *int.

var ip *int = flag.Int("flagname", 1234, "help message for flagname")

If you like, you can bind the flag to a variable using the Var() functions.

var flagvar int
func init() {
    flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname")
}

Or you can create custom flags that satisfy the Value interface (with pointer receivers) and couple them to flag parsing by

flag.Var(&flagVal, "name", "help message for flagname")

For such flags, the default value is just the initial value of the variable.

After all flags are defined, call

flag.Parse()

to parse the command line into the defined flags.

Flags may then be used directly. If you're using the flags themselves, they are all pointers; if you bind to variables, they're values.

fmt.Println("ip has value ", *ip)
fmt.Println("flagvar has value ", flagvar)

There are helper functions available to get the value stored in a Flag if you have a FlagSet but find it difficult to keep up with all of the pointers in your code. If you have a pflag.FlagSet with a flag called 'flagname' of type int you can use GetInt() to get the int value. But notice that 'flagname' must exist and it must be an int. GetString("flagname") will fail.

i, err := flagset.GetInt("flagname")

After parsing, the arguments after the flag are available as the slice flag.Args() or individually as flag.Arg(i). The arguments are indexed from 0 through flag.NArg()-1.

The pflag package also defines some new functions that are not in flag, that give one-letter shorthands for flags. You can use these by appending 'P' to the name of any function that defines a flag.

var ip = flag.IntP("flagname", "f", 1234, "help message")
var flagvar bool
func init() {
	flag.BoolVarP(&flagvar, "boolname", "b", true, "help message")
}
flag.VarP(&flagVal, "varname", "v", "help message")

Shorthand letters can be used with single dashes on the command line. Boolean shorthand flags can be combined with other shorthand flags.

The default set of command-line flags is controlled by top-level functions. The FlagSet type allows one to define independent sets of flags, such as to implement subcommands in a command-line interface. The methods of FlagSet are analogous to the top-level functions for the command-line flag set.

Setting no option default values for flags

After you create a flag it is possible to set the pflag.NoOptDefVal for the given flag. Doing this changes the meaning of the flag slightly. If a flag has a NoOptDefVal and the flag is set on the command line without an option the flag will be set to the NoOptDefVal. For example given:

var ip = flag.IntP("flagname", "f", 1234, "help message")
flag.Lookup("flagname").NoOptDefVal = "4321"

Would result in something like

Parsed Arguments Resulting Value
--flagname=1357 ip=1357
--flagname ip=4321
[nothing] ip=1234

Command line flag syntax

--flag    // boolean flags, or flags with no option default values
--flag x  // only on flags without a default value
--flag=x

Unlike the flag package, a single dash before an option means something different than a double dash. Single dashes signify a series of shorthand letters for flags. All but the last shorthand letter must be boolean flags or a flag with a default value

// boolean or flags where the 'no option default value' is set
-f
-f=true
-abc
but
-b true is INVALID

// non-boolean and flags without a 'no option default value'
-n 1234
-n=1234
-n1234

// mixed
-abcs "hello"
-absd="hello"
-abcs1234

Flag parsing stops after the terminator "--". Unlike the flag package, flags can be interspersed with arguments anywhere on the command line before this terminator.

Integer flags accept 1234, 0664, 0x1234 and may be negative. Boolean flags (in their long form) accept 1, 0, t, f, true, false, TRUE, FALSE, True, False. Duration flags accept any input valid for time.ParseDuration.

Mutating or "Normalizing" Flag names

It is possible to set a custom flag name 'normalization function.' It allows flag names to be mutated both when created in the code and when used on the command line to some 'normalized' form. The 'normalized' form is used for comparison. Two examples of using the custom normalization func follow.

Example #1: You want -, _, and . in flags to compare the same. aka --my-flag == --my_flag == --my.flag

func wordSepNormalizeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName {
	from := []string{"-", "_"}
	to := "."
	for _, sep := range from {
		name = strings.Replace(name, sep, to, -1)
	}
	return pflag.NormalizedName(name)
}

myFlagSet.SetNormalizeFunc(wordSepNormalizeFunc)

Example #2: You want to alias two flags. aka --old-flag-name == --new-flag-name

func aliasNormalizeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName {
	switch name {
	case "old-flag-name":
		name = "new-flag-name"
		break
	}
	return pflag.NormalizedName(name)
}

myFlagSet.SetNormalizeFunc(aliasNormalizeFunc)

Deprecating a flag or its shorthand

It is possible to deprecate a flag, or just its shorthand. Deprecating a flag/shorthand hides it from help text and prints a usage message when the deprecated flag/shorthand is used.

Example #1: You want to deprecate a flag named "badflag" as well as inform the users what flag they should use instead.

// deprecate a flag by specifying its name and a usage message
flags.MarkDeprecated("badflag", "please use --good-flag instead")

This hides "badflag" from help text, and prints Flag --badflag has been deprecated, please use --good-flag instead when "badflag" is used.

Example #2: You want to keep a flag name "noshorthandflag" but deprecate its shortname "n".

// deprecate a flag shorthand by specifying its flag name and a usage message
flags.MarkShorthandDeprecated("noshorthandflag", "please use --noshorthandflag only")

This hides the shortname "n" from help text, and prints Flag shorthand -n has been deprecated, please use --noshorthandflag only when the shorthand "n" is used.

Note that usage message is essential here, and it should not be empty.

Hidden flags

It is possible to mark a flag as hidden, meaning it will still function as normal, however will not show up in usage/help text.

Example: You have a flag named "secretFlag" that you need for internal use only and don't want it showing up in help text, or for its usage text to be available.

// hide a flag by specifying its name
flags.MarkHidden("secretFlag")

Disable sorting of flags

pflag allows you to disable sorting of flags for help and usage message.

Example:

flags.BoolP("verbose", "v", false, "verbose output")
flags.String("coolflag", "yeaah", "it's really cool flag")
flags.Int("usefulflag", 777, "sometimes it's very useful")
flags.SortFlags = false
flags.PrintDefaults()

Output:

  -v, --verbose           verbose output
      --coolflag string   it's really cool flag (default "yeaah")
      --usefulflag int    sometimes it's very useful (default 777)

Supporting Go flags when using pflag

In order to support flags defined using Go's flag package, they must be added to the pflag flagset. This is usually necessary to support flags defined by third-party dependencies (e.g. golang/glog).

Example: You want to add the Go flags to the CommandLine flagset

import (
	goflag "flag"
	flag "github.com/spf13/pflag"
)

var ip *int = flag.Int("flagname", 1234, "help message for flagname")

func main() {
	flag.CommandLine.AddGoFlagSet(goflag.CommandLine)
	flag.Parse()
}

More info

You can see the full reference documentation of the pflag package at godoc.org, or through go's standard documentation system by running godoc -http=:6060 and browsing to http://localhost:6060/pkg/github.com/spf13/pflag after installation.

pflag's People

Contributors

alecthomas avatar andronat avatar bep avatar chris-serafin avatar cyberax avatar dnephin avatar eparis avatar fedosin avatar filbranden avatar johnschnake avatar leitzler avatar mckern avatar moorereason avatar mrbanzai avatar mweibel avatar n10v avatar noahdietz avatar ogier avatar rajatjindal avatar riobard avatar rvolosatovs avatar shomron avatar spf13 avatar stevenroose avatar tamalsaha avatar technoweenie avatar therealmitchconnors avatar thockin avatar wu8685 avatar xilabao avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pflag's Issues

Document the AddGoFlagSet method in the README file

Flags defined with Go's flag package can be added to the pflag's flag set using the AddGoFlagSet method. This is necessary when using libraries that declare flags, such as glang/glog.

It took me a while to realize that I had to add the go flagset to the pflag flag set. Developers new to this library might benefit from documentation around this.

arguments after boolean flag cannot be retrieved via Args()

When putting a boolean flag at last just before arguments, it seems that pflag sees the argument as value supplied to the boolean flag instead of separate argument.

lMode := pflag.BoolP("", "l", false, "use a long listing format")
pflag.Parse(args)
argArray := pflag.Args()

When calling like

ls -l /

argArray does not contains "/"

Calling Set on StringSlice flag appends result rather than setting it

I have a question regarding calling Set on a string slice flag.

Running the following code against master produces the following result

package main

import (
  "fmt"
  "github.com/spf13/pflag"
)

func main() {
  f := pflag.StringSlice("test", []string{"a"}, "")
  fmt.Printf("1: %v\n", f) // Expected: "[a]" Actual :"[a]"
  pflag.Set("test", "b")
  fmt.Printf("2: %v\n", f) // Expected: "[b]" Actual :"[b]"
  pflag.Set("test", "c")
  fmt.Printf("3: %v\n", f) // Expected: "[c]" Actual :"[b,c]"
}
$ go run main.go 
1: &[a]
2: &[b]
3: &[b c]

I expect that when I call Set on the flag test I overwrite whatever value it was before. However, the second call to Set actually appends to the existing slice. Is this behaviour intended?

Offer easy way to override FlagUsages' format string

The format string in the FlagUsages method of FlagSet currently is set like this...

format = format + ": %s\n"

It would be nice to be able to set this to be tab separated instead.

format = format + ": \t%s\n"

It would be nice to be able to set the delimiter somewhere instead of having to override this by reimplementing (copy & pasting) the entire method.

Don't use "slice" word for --help of slice flags

(I'm using pflag via cobra and) if I have a StringSlice flag, I get this in --help:

  -r, --role stringSlice   Description of this flag bla bla bla

stringSlice flag is coming from pflag. It doesn't make sense to anyone who's not using Go, which just happens to be the programming language the command line tool is written in.

I think this should read:

  -r, --role strings...   Description of this flag bla bla bla

or

  -r, --role=string [-r,--role=string[, ...]   Description of this flag bla bla bla

or some sort of better notation indicating the flag can be used for multiple times. Right now it's too cryptic (and forcing me to add explanation to the description of the flag).

Changed flag order after setting normalize func

Consider this script:

package main

import (
	"fmt"

	flag "github.com/spf13/pflag"
)

func main() {
	f := flag.NewFlagSet("BUG", flag.ContinueOnError)
	f.Bool("B", true, "")
	f.Bool("C", true, "")
	f.Bool("D", true, "")
	f.Bool("A", true, "")
	f.SortFlags = false
	f.SetNormalizeFunc(func(f *flag.FlagSet, name string) flag.NormalizedName { return flag.NormalizedName(name) })
	f.VisitAll(func(f *flag.Flag) {
		fmt.Println(f.Name)
	})
}

Expected output:

B
C
D
A

Actual output:

D
A
B
C

And the output is always different.

Grouping flags?

Hi,

is it possible to group flags so that they appear together in the --help text? I'm using pflag in my backup program, there I have for example the options to expire backups according to a policy. Before (with the go-flags) library the options were nicely grouped together (in the order they were defined):

$ restic forget --help
Usage:
  restic [OPTIONS] forget [snapshot ID] ...
[...]
[forget command options]
      -l, --keep-last=    keep the last n snapshots
      -H, --keep-hourly=  keep the last n hourly snapshots
      -d, --keep-daily=   keep the last n daily snapshots
      -w, --keep-weekly=  keep the last n weekly snapshots
      -m, --keep-monthly= keep the last n monthly snapshots
      -y, --keep-yearly=  keep the last n yearly snapshots
          --keep-tag=     alwaps keep snapshots with this tag (can be specified multiple times)
          --hostname=     only forget snapshots for the given hostname
          --tag=          only forget snapshots with the tag (can be specified multiple times)
      -n, --dry-run       do not delete anything, just print what would be done

Now with pflag and cobra they are printed in alphabetical order:

[...]
Usage:
  restic forget [flags] [snapshot ID] [...]

Flags:
  -n, --dry-run                do not delete anything, just print what would be done
      --hostname string        only forget snapshots for the given hostname
  -d, --keep-daily int         keep the last n daily snapshots
  -H, --keep-hourly int        keep the last n hourly snapshots
  -l, --keep-last int          keep the last n snapshots
  -m, --keep-monthly int       keep the last n monthly snapshots
      --keep-tag stringSlice   always keep snapshots with this tag (can be specified multiple times)
  -w, --keep-weekly int        keep the last n weekly snapshots
  -y, --keep-yearly int        keep the last n yearly snapshots
      --tag stringSlice        only forget snapshots with the tag (can be specified multiple times)
[...]

Is there a way to group some flags so that they are printed together?

Error Handling is Not Flexible

The *OnError mechanism for how to handle errors is insufficient. We now have #160 which wants to ignore unknowns. Using the current error handling options we would need to add IgnoreUnknown to all of the existing OnError types (aka we would need ContinueIgnoreUnknownOnError, ExitIgnoreUnknownOnError, PanicIgnoreUnknonwOnError, etc. Every new 'option' we want to have on Errors will double the number of defines. This is not the best API design.

Errors are printed more than once

I happened to notice errors are printed more than once (once on stderr, once on stdout)

This is due to an extra fmt.Println where there should be none. (Or so I believe)

pflag/flag.go

Line 1065 in 4c012f6

fmt.Println(err)

The correct error output is in

pflag/flag.go

Line 881 in 4c012f6

fmt.Fprintln(f.out(), err)

If you confirm that the first println is indeed an error or someone who forgot one of their println debug statement, I will gladly push a fix.

Happy new year!

Travis golint handling breaks on go v1.3 and 1.4

For v1.3 and v1.4 travis fails due to changes in golint and assumed golang.org/x structure.

Ref: golang/lint#178

Example output from travis:

2.18s$ eval "$(gimme 1.3)"
go version go1.3 linux/amd64
$ export GOPATH=$HOME/gopath
$ export PATH=$HOME/gopath/bin:$PATH
$ mkdir -p $HOME/gopath/src/github.com/spf13/pflag
$ rsync -az ${TRAVIS_BUILD_DIR}/ $HOME/gopath/src/github.com/spf13/pflag/
$ export TRAVIS_BUILD_DIR=$HOME/gopath/src/github.com/spf13/pflag
$ cd $HOME/gopath/src/github.com/spf13/pflag
$ gimme version
v0.2.2
$ go version
go version go1.3 linux/amd64
go.env
$ go env
GOARCH="amd64"
GOBIN=""
GOCHAR="6"
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/travis/gopath"
GORACE=""
GOROOT="/home/travis/.gimme/versions/go1.3.linux.amd64"
GOTOOLDIR="/home/travis/.gimme/versions/go1.3.linux.amd64/pkg/tool/linux_amd64"
CC="gcc"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0"
CXX="g++"
CGO_ENABLED="1"
2.14s$ go get github.com/golang/lint/golint
package github.com/golang/lint/golint
    imports go/types: unrecognized import path "go/types"
package github.com/golang/lint/golint
    imports golang.org/x/tools/go/gcimporter15
    imports golang.org/x/tools/go/gcimporter15
    imports golang.org/x/tools/go/gcimporter15: no buildable Go source files in /home/travis/gopath/src/golang.org/x/tools/go/gcimporter15
The command "go get github.com/golang/lint/golint" failed and exited with 1 during .
Your build has been stopped.

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

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.

Question about optional params

Hi,
I need to implement an optional param in my programs and I am wondering if there is a way to do it using this library.
The use case is to mimic MySQL behavior for -p
-p can be used as -p<password here> or just -p and in that case, the param is marked as set but empty and MySQL client will ask for a password. This way, you can avoid using a password in the command line.

Regards

Help for subcommands are not working as expected.

I had a sample cli with go flags implemented, now I just replaced "flag" with "flag "github.com/spf13/pflag""

$command subcommand --help

This command still displays the main command's help.
The same works fine with the go flags.

Tag a new release: v1.0.2

In order to use our dependency tool in a clean way, is it possible to tag a new release? We would love to see this base64 change included in that one!

FlagSet.Parse behaviour changed between v1.0.0 and v1.0.1

Somewhere between these 2 releases there was a change in behaviour.

In v1.0.0 an invalid flag causes flag.Parse() to print Usage and print the error (unknown shorthand flag...), as well as return the error.

In v1.0.1 an invalid flag causes flag.Parse() to return the error, but print nothing (which makes it very confusing for a user because there is no error printed anymore).

I'm using flags.ContinueOnError and flags.SetInterspersed(false), but I'm not sure if it's specific to either of those.

I think the change comes from #138. Although the diff seems like it's doing the opposite.

I actually like the new behaviour better, but the change was surprising from 1.0.0 to v1.0.1 without any mention of it in the release notes.

Parse string map values

In cobra, I have a use case where I want to parse a string map from command line as follow:

$ mycommand --string-map-option "a=b,c=d"

I haven't seen any method into pflag to parse the result as map[string]string.

For now, I have implemented in my project a basic version that satisfies pflag.Value interface. I use FlagSet.Var() method to use the previous value and it's working well.

It will be interesting to directly have this parser into pflag package. I think this flag parser can be helpful to the community.
If you are OK with this idea, I can propose a Pull Request based on my current implementation.

Humbly requesting a release...

Hi folks,

I'd love to get the new Add map valued (string->string, string->int) flags (see below for the diff) without having to point to the master branch. This is so useful, any chance we could see a release soonish?

v1.0.2...master

Add a new type StringArray to handle multiple occurances of a flag without comma separation

Recently, there is a type StringSlice to handle the flag which is inputed multiple times. But it also handle each argument by comma separation without escaping. It is unexpected in StringArray.
For example

# input:
--value=one,two --value=three

# result by StringSlice
["one", "two", "three"]

# result expected by StringArray
["one,two", "three"]

A discussion about this function is here

NoOptDefVal doesn't work with shorthand

var flagSet flag.FlagSet

flagSet.StringVarP(&creds, "user", "u", "", "Login Credentials: ")
flagSet.StringVarP(&passwd, "password", "p", "", "Password for User.")
flagSet.StringVarP(&lastname, "lastName", "f", "", "First Name"
flagSet.StringVarP(&lastname, "lastName", "l", "", "Last Name"
flagSet.Lookup("silent").NoOptDefVal = "Fred Flintstone"
...
When I run the program:
myProgram --firstName=Barney --lastName=Rubble -u brubble -p b3dr0ck,
it works.
When I run the program:
myProgram -f Barney -lastName Rubble -u brubble -p b3dr0ck,
This breaks things. All options past the -f will not be accepted and a value will not get assigned to firstName or lastName variable.s

RFE: Support Go 1.5 flag 'nicer usage message' style usage messages

The standard Go flag package was changed to have significantly nicer and more Unix-style flag usage messages in commit golang/go@51d6660 (with some subsequent refinements). It would be nice if pflag could be updated to this style as well to match flag.

(If this is of interest, I can take a shot at porting the changes from the flag package to pflag and submitting them as a pull request.)

Difference between Slice and Array flags

first of all thanks for this awesome flag library. I use it through cobra which is amazing as well 👍

I have my issues reading the documentation about the difference between Slice and Array flags. Could you point that out to me? I'd be more than happy to provide a PR with doc changes once I'm understanding what the difference is 😄

Thanks!

Parsing of the flags with NoOptDefValue is not considering the value

Consider this code:

package main

import (
	"fmt"
	"os"

	"github.com/spf13/pflag"
)

var b bool

func main() {
	fs := pflag.NewFlagSet("", pflag.ContinueOnError)
	fs.BoolVar(&b, "boolflag", true, "")
	fs.Parse(os.Args)
	fmt.Println(b)
}

Expected:

$ go run bug.go --boolflag false
false

Got:

$ go run bug.go --boolflag false
true

Using pflag precludes the use of any other package written to use flag

This is because if you mix packages that use pflag with those that use flag, it requires you to call both flag.Parse() anf pflag.Parse() - one or both of which will fail.

This seems to be a crippling requirement because if I choose to use pflag, I am precluded from using any other third party package that uses flag.

Inconsistent behavior for StringArrayVar

In string_array_test.go, the test TestEmptySAValue checks that the string slice is empty when the flag content is empty (--sa= -> [])

However, when using an empty flag along with at least one non-empty, the empty value is in the slice (--sa=A --sa= -> ["A", ""])

This seems to be inconsistent.

StringSlice flag and default values

So running this code with the latest master, seem to add any value you give on the commandline to the default set as defined in the flag. Is this the intended behavior? (This is also new behavior - didn't git bisect)

package main

import (
    "fmt"

    "github.com/spf13/pflag"
)

func main() {
    fFlag := pflag.StringSlice("a", []string{"1", "3"}, "a bogus option")
    pflag.Parse()
    fmt.Printf("%v\n", fFlag)
}

Running:

% go run flagtest.go --a 2
&[1 3 2]

When using "go test -v", I get "unknown shorthand flag: 't' in -test.v=true"

I have a project that imports and uses pflag.
Whenever I run tests with the "-v" flag , I get "unknown shorthand flag: 't' in -test.v=true" and also a print of the help text for the flags defined with pflag.
Without "-v" tests succeed.

Searching I have found the following PR in Cobra to seem to be related : spf13/cobra#155
Since Cobra also imports pflag, is there either a way to fix pflag or alternatively, some work around I can add to my own program?

Strange interaction with Count and cobra

Compile this code.

Here is the guts of it

var (
	verbose = pflag.CountP("verbose", "v", "Print more stuff")

	RootCmd = &cobra.Command{
		Use: "test",
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Printf("ROOT verbose = %d, args = %v\n", *verbose, args)
		},
	}

	testCmd = &cobra.Command{
		Use: "test",
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Printf("TEST verbose = %d, args = %v\n", *verbose, args)
		},
	}
)

func init() {
	RootCmd.AddCommand(testCmd)
}

func main() {
	if err := RootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}
}

Now run with various arguments

Note this has run the ROOT command not the TEST command

$ ./flags -v test
ROOT verbose = 1, args = [test]

This is OK

$ ./flags test potato
TEST verbose = 0, args = [potato]

Why does this produce an error?

$ ./flags -v test potato
Error: unknown command "potato" for "test"
Run 'test --help' for usage.
unknown command "potato" for "test"

This seems to work though

$ ./flags -vv test potato
TEST verbose = 2, args = [potato]

It is as if the -v on it's own eats an argument, but it isn't as simple as that as this works fine

 ./flags -v potato
ROOT verbose = 1, args = [potato]

Consider semantic versioning

So, the time has come to start to introduce semantic versioning since this is the only "standard" that the GO community have been pushing for. Many of the package managers out there are using semantic versioning to determine if a newer release is available. Due to this, please consider adding a tagged release to this repo (and all other you have that don't have tagged versions). This would REALLY help us that relies on package managers du to project size A LOT! :)

Backwards incompatibility with flag package

Single dash long options are not supported in pflag which yields a backwards incompatibility with the flag package and thus is not a true drop-in replacement. This also affects those moving from multiconfig, etc. It would be good to be able to optionally support single dash long options (e.g., -option vs only --option) for those that need a drop in replacement with extended features.

See PR #146

Override usage message

Would it make sense to override the usage message of a flag like overriding default values? Is there any way currently to do it?

Customizing help text for slices?

Hi,

the text that is printed after e.g. a string slice option is not helpful for end-users who do not know Go. For example, defining a flag like this:

var tags []string

func init() {
    f.StringSliceVar(&tags, "tag", []string{}, "only forget snapshots with the tag (can be specified multiple times)")
}

is printed in the help text in the following way:

  -y, --keep-yearly int        keep the last n yearly snapshots
      --tag stringSlice        only forget snapshots with the tag (can be specified multiple times)

That's not really helpful. Is there a way to define the text that is printed after the flag?

Default values for custom fields broken

Before

$ rclone-v1.30 -h 2>&1| grep chunk-size
      --b2-chunk-size value            Upload chunk size. Must fit in memory. (default 96M)
      --drive-chunk-size value         Upload chunk size. Must a power of 2 >= 256k. (default 8M)
      --dropbox-chunk-size value       Upload chunk size. Max 150M. (default 128M)
      --onedrive-chunk-size value      Above this size files will be chunked - must be multiple of 320k. (default 10M)
      --swift-chunk-size value         Above this size files will be chunked into a _segments container. (default 5G)

After

$ rclone-v1.33 -h | grep chunk-size
      --b2-chunk-size int               Upload chunk size. Must fit in memory.
      --drive-chunk-size int            Upload chunk size. Must a power of 2 >= 256k.
      --dropbox-chunk-size int          Upload chunk size. Max 150M.
      --onedrive-chunk-size int         Above this size files will be chunked - must be multiple of 320k.
      --swift-chunk-size int            Above this size files will be chunked into a _segments container.

No defaults :-(

I've used git bisect to discover the problematic commit f90e8bb by @moorereason

The above is controlled by a custom value fs.SizeSuffix

See rclone/rclone#706 for background

pflag.Args() Doesn't work in FlagSet.

I have the following code:

	flagSet.StringVarP(&port, "port", "p", "", "Port to scan")
	flagSet.BoolVarP(&showOpen, "open", "o", false, "Only show open ports.")
	flagSet.BoolVarP(&verb, "verbose", "v", false, "Verbose")
	flagSet.BoolVarP(&help, "help", "h", false, "help")
	flagSet.StringVarP(&outF, "format", "F", "gridt", "Format: grid, gridt, tab ")
	flagSet.StringVarP(&timeout, "timeout", "t", "500ms", "Timeout value 5s= 5 seconds, 5ms=5 milliseconds and so on (5ns, 5us")
	flagSet.StringVarP(&file, "file", "f", "", "A filename containing a list of IP addresses to scan, separated by newlines.")
	flagSet.Parse(os.Args[1:])
	ipaddr = flagSet.Args()

When I use this, other flags in the command line do not work.

] $ binaries/mac/tcpscan 135.91.207.148/29 --port 22,23 
+-------------------+---------+-------------+-------------+
|           Address |    Port |      Status |        Time |
+===================+=========+=============+=============+
|    135.91.207.145 |      22 |    Filtered |    504.02ms |
|    135.91.207.146 |      22 |    Filtered |    504.23ms |
|    135.91.207.147 |      22 |        Open |    163.32ms |
|    135.91.207.148 |      22 |        Open |    167.41ms |
|    135.91.207.149 |      22 |    Filtered |    503.72ms |
|    135.91.207.150 |      22 |    Filtered |    503.72ms |
|            --port |      22 |      Closed |     10.41ms |
|                22 |      23 |          22 |      Closed |
|                -v |      22 |      Closed |     53.39ms |
+-------------------+---------+-------------+-------------+

(By default, if no port is given, it tries port 22 -- Its not picking up the "--port 22,23" that was passed.)

When I don't use a FlagSet it works fine:

Code:

	flag.StringVarP(&port, "port", "p", "", "Port to scan")
	flag.BoolVarP(&showOpen, "open", "o", false, "Only show open ports.")
	flag.BoolVarP(&verb, "verbose", "v", false, "Verbose")
	flag.BoolVarP(&help, "help", "h", false, "help")
	flag.StringVarP(&outF, "format", "F", "gridt", "Format: grid, gridt, tab ")
	flag.StringVarP(&timeout, "timeout", "t", "500ms", "Timeout value 5s= 5 seconds, 5ms=5 milliseconds and so on (5ns, 5us")
	flag.StringVarP(&file, "file", "f", "", "A filename containing a list of IP addresses to scan, separated by newlines.")
	flag.Parse()
        ipaddr = flag.Args()

Output:

] $ binaries/mac/tcpscan 135.91.207.148/29 --port 22,23
+-------------------+---------+-------------+-------------+
|           Address |    Port |      Status |        Time |
+===================+=========+=============+=============+
|    135.91.207.145 |      22 |    Filtered |    503.13ms |
|    135.91.207.145 |      23 |    Filtered |    503.12ms |
|    135.91.207.146 |      22 |    Filtered |    503.29ms |
|    135.91.207.146 |      23 |    Filtered |    503.19ms |
|    135.91.207.147 |      22 |        Open |    172.62ms |
|    135.91.207.147 |      23 |    Filtered |    503.27ms |
|    135.91.207.148 |      22 |        Open |    176.30ms |
|    135.91.207.148 |      23 |    Filtered |    503.24ms |
|    135.91.207.149 |      22 |    Filtered |    503.20ms |
|    135.91.207.149 |      23 |    Filtered |    503.34ms |
|    135.91.207.150 |      22 |    Filtered |    503.42ms |
|    135.91.207.150 |      23 |    Filtered |    503.20ms |
+-------------------+---------+-------------+-------------+

I need this as I want to hide a few options using FlagSet.MarkHidden.

Issue with StringSliceVar and commas

Hello,

We're using StringSlice in our CLI and we're having an issue with commas in the values. Here is an example:

// command
tool --env="FOO=BAR" --env="BAZ=a string, with, commas"

// configuration
cmd.cobra.Flags().StringSliceVar(&cmd.additionalEnv, "env", []string{}, "additional environment variables, in the form VARIABLE=VALUE")

// current result
[]string{"FOO=BAR", "BAZ=a string", " with", " commas"}

// expected result
[]string{"FOO=BAR", "BAZ=a string, with, commas"}

I noticed the value is split with commas here: https://github.com/spf13/pflag/blob/master/string_slice.go#L24
Is there a way to get around it ?
We're not using the form --env=foo,bar,baz

POSIX compliance

I have noticed that programs using this package require the use of 2 '-' delimiters for single character, when the standard is using just 1 '-' delimiter for these single character options. This makes it confusing for many users, decreasing usability. See POSIX.1-2017 command line argument conventions(single character flags) and the GNU extension of these conventions(multi-character flags) for more details

Ability to auto prefix flag subset

Hello!

Is it possible to get the "fork" of the *pflag.FlagSet with each call to, say, f.VarP become auto-prefixed?

I mean something like this:

flag := pflag.NewFlagSet(...) // As usual.
flag.Int("my-cool-int", 42, "some int")

subFlag := flag.SubSet("prefix")
subFlag.Int("my-cool-int", 24, "another int") // Actually become `prefix.my-cool-int`.

Thanks.

Add support for repeated "counter" flags

As a developer I want to give my users the option to set the log level of my application without knowing about internal numberings of log levels. A common practice is a repeated -v flag to increase the log level.

Example:

pflag.CounterFlagP(&loglevel, "verbose", "v", 0, "Increase log level")

# ./myapp
loglevel == 0

# ./myapp -v
loglevel == 1

# ./myapp -vvvv
loglevel == 4

Default value used as a +n for the count of used flag or left out?

Keep hold of unknown flags if they are allowed

When FlagSet::ParseErrorsWhitelist.UnknownFlags = true unknown flags are thrown away.

I propose adding a map of UnknownFlags to FlagSet which is populated when the option (above) is set

The map would be keyed on flag name and keep any and all values found as strings (given there is no sensible way to try and guess their type)

The use case (for me) is an capturing name value pairs that are dependent on dynamic templates.

I have a work in progress version for which I will submit a PR

Setting `NoOptDefVal` forces using `=` syntax

Pflags accepts the following syntaxes for an integer argument:

--verbose=0
--verbose 0
-v 0
-v=0

Using this program:

v := pflag.IntP("verbose", "v", 1, "make output verbose")
pflag.Parse()
fmt.Printf("verbosity: %d\n", *v)

I get the following results:

$ ./test
verbosity: 1
$ ./test --verbose=0
verbosity: 0
$ ./test --verbose 2
verbosity: 2
$ ./test -v 0
verbosity: 0
$ ./test -v=2
verbosity: 2

If I set NoOptDefVal, I get inconsistent behavior.

v := pflag.IntP("verbose", "v", 1, "make output verbose")
verboseFlag := pflag.Lookup("verbose")
// In case of --verbose or -v with no arg, set to 2
verboseFlag.NoOptDefVal = "2"
pflag.Parse()

fmt.Printf("verbosity: %d\n", *v)

Here are the results:

$ ./test
verbosity: 1
$ ./test --verbose=0
verbosity: 0
$ ./test --verbose 2
verbosity: 2
$ ./test --verbose 3
verbosity: 2
$ ./test -v=0
verbosity: 0
$ ./test -v=1
verbosity: 1
$ ./test -v=2
verbosity: 2
$ ./test -v 1
verbosity: 2

I expected -v 3 and -v=3 to work the same way.

Is this a bug, or am I missing the point of NoOptDefVal ?

Allow short flags without long form

Sometimes it can be preferable to only provide a short flag, currently this is not possible (AFAICT).

We can try to use an empty string for the long form:

noop = flag.BoolP("", "n", false, "Noop")

But the result is a bit unexpected:

$ test -h
Usage of test:
  -n, --          Noop

If we try to define a second flag in the same manner, the result is a panic:

panic: test flag redefined:

Ideally the output would look like this:

Usage of test:
  -n              Noop
  -h, --help      Print this help message and exit
      --version   Print version information and exit

I think the biggest problem is how to cleanly implement this in the current API, just using an empty string for the name seems a bit weird. Thoughts?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.