[flag] Multi-valued argument

1,181 views
Skip to first unread message

Stéphane Bortzmeyer

unread,
Mar 18, 2010, 3:38:32 PM3/18/10
to golang-nuts
A program may accept several values of an argument. Apparently, flag
does not accept it:

% ./server -address=192.168.1.1 -address=192.168.1.2
flag specified twice: -address

The only solution I see is to define a separator in the argument and
to parse myself:

/server -address=192.168.1.1\;192.168.1.2

Is there a feature in the flag package to do it more simply?

Rob 'Commander' Pike

unread,
Mar 18, 2010, 5:30:13 PM3/18/10
to Stéphane Bortzmeyer, golang-nuts

No, but it's easy to split the flag value yourself using
strings.Split. I suggest commas rather than semicolons for simpler
typing, too.

If this is important to fix - and I don't think it is - the flag value
for "address" would have to be a slice. It's doable but it's easier
just to split the flag value.

-rob


jshipley

unread,
Mar 29, 2010, 12:33:14 PM3/29/10
to golang-nuts
On Mar 18, 3:30 pm, "Rob 'Commander' Pike" <r...@google.com> wrote:
> On Mar 18, 2010, at 12:38 PM, Stéphane Bortzmeyer wrote:
>
> > A program may accept several values of an argument. Apparently,flag
> > does not accept it:
>
> > % ./server -address=192.168.1.1 -address=192.168.1.2
> >flagspecified twice: -address

>
> > The only solution I see is to define a separator in the argument and
> > to parse myself:
>
> > /server -address=192.168.1.1\;192.168.1.2
>
> > Is there a feature in theflagpackage to do it more simply?
>
> No, but it's easy to split theflagvalue yourself using  

> strings.Split.  I suggest commas rather than semicolons for simpler  
> typing, too.
>
> If this is important to fix - and I don't think it is - theflagvalue  
> for "address" would have to be a slice. It's doable but it's easier  
> just to split theflagvalue.
>
> -rob

This feature is enough to make me switch to another option parsing
library for some projects.

If a patch was submitted adding these two functions, what are the
chances that it would be accepted?
StringArray(name string, value []string, usage string) *[]string
StringArrayVar(p *[]string, name string, value []string, usage string)

This assumes, of course, that the patch is good. I'm mostly asking if
the API change would be accepted.

Russ Cox

unread,
Mar 29, 2010, 12:53:08 PM3/29/10
to jshipley, golang-nuts
I'm skeptical about this API change.
Every flag package is different, of course,
but they pretty consistently implement the
rule that repeating a flag overrides whatever
the earlier setting was. I think that users
familiar with the common flag interfaces
expect --foo=bar --foo=other to be like
--foo=other, not like --foo=bar,other.

Russ

roger peppe

unread,
Mar 29, 2010, 1:00:08 PM3/29/10
to r...@golang.org, jshipley, golang-nuts

the -I argument to 8g is one obvious counter-example.

chris dollin

unread,
Mar 29, 2010, 1:11:45 PM3/29/10
to r...@golang.org, jshipley, golang-nuts

Count me as someone who has implemented a flags-like
argument processing package that specifically has
-thing x -thing y give the thing variable the list of values
x, y .

(In Java, not Go)

(Yes, I know I'm weird)

--
Chris "allusive" Dollin

Rob 'Commander' Pike

unread,
Mar 29, 2010, 1:22:05 PM3/29/10
to jshipley, golang-nuts

On Mar 29, 2010, at 9:33 AM, jshipley wrote:

> On Mar 18, 3:30 pm, "Rob 'Commander' Pike" <r...@google.com> wrote:
>> On Mar 18, 2010, at 12:38 PM, Stéphane Bortzmeyer wrote:
>>
>>> A program may accept several values of an argument. Apparently,flag
>>> does not accept it:
>>
>>> % ./server -address=192.168.1.1 -address=192.168.1.2
>>> flagspecified twice: -address
>>
>>> The only solution I see is to define a separator in the argument and
>>> to parse myself:
>>
>>> /server -address=192.168.1.1\;192.168.1.2
>>
>>> Is there a feature in theflagpackage to do it more simply?
>>
>> No, but it's easy to split theflagvalue yourself using
>> strings.Split. I suggest commas rather than semicolons for simpler
>> typing, too.
>>
>> If this is important to fix - and I don't think it is - theflagvalue
>> for "address" would have to be a slice. It's doable but it's easier
>> just to split theflagvalue.
>>
>> -rob
>
> This feature is enough to make me switch to another option parsing
> library for some projects.

Really? It's worth that much trouble to avoid one call to strings.Split?

>
> If a patch was submitted adding these two functions, what are the
> chances that it would be accepted?
> StringArray(name string, value []string, usage string) *[]string
> StringArrayVar(p *[]string, name string, value []string, usage string)
> This assumes, of course, that the patch is good. I'm mostly asking if
> the API change would be accepted.

It would need an explanation for why it's valuable. I haven't seen
one yet.

-rob

roger peppe

unread,
Mar 29, 2010, 1:28:41 PM3/29/10
to Rob 'Commander' Pike, jshipley, golang-nuts
On 29 March 2010 17:22, Rob 'Commander' Pike <r...@google.com> wrote:
>> This feature is enough to make me switch to another option parsing
>> library for some projects.
>
> Really? It's worth that much trouble to avoid one call to strings.Split?

strings.Split isn't a great solution if the flag arguments are
being used to pass names that may contain the separator
character (as filenames may contain commas or white space
for example)

e.g. 8g -I "$a" -I "$b"

Rob 'Commander' Pike

unread,
Mar 29, 2010, 1:40:47 PM3/29/10
to roger peppe, jshipley, golang-nuts

8g comes from a different era (using the term advisedly) in flag
processing.

-rob

roger peppe

unread,
Mar 29, 2010, 2:04:35 PM3/29/10
to Rob 'Commander' Pike, jshipley, golang-nuts

i realise that, but i still think the point is valid. if you want to split an
argument, you either have to ban at least one character or invent
a new quoting rule.

if 8g was invented now using your suggestion above,
then a pathname with a comma would cause a failure.

also this idiom:

CFLAGS=($CFLAGS -I $newdir)

to add another directory to the search path
would be harder to write. then again it probably
fails in most shells anyway if $newdir contains white space.

in rc (ok i know i'm in a minority here) i can easily prepend
a flag to each of several arguments:

dirs = /foo/bar/*.h
cmd '-I='^$h

but reliably concatenating them separated by a comma is harder.
i imagine it might be easier in bash though.

roger peppe

unread,
Mar 29, 2010, 5:45:09 PM3/29/10
to Rob 'Commander' Pike, jshipley, golang-nuts
mostly for my own amusement, and with some ideas from
others, i knocked up a quick sketch
of a slightly different approach (attached) to the flag package.

the main difference is that flag arguments starting
with "-" need to be specified as "-flag=value", not "-flag value".

this means that flag parsing is context insensitive.

the implementation allows the use of []type to
get all values for a given flag. if the type implements
Parseable, then its Parse method is called to do the
conversion from string.

here's an example. note that global variables can depend
command-line flags; even the default value of a flag
can depend on a command-line flag!

package main
import (
"fmt"
"flags"
)

type Xs int
// silly parser - count the number of 'x's in the string
func (x Xs) Parse(s string) (interface{}, bool) {
n := Xs(0)
for _, c := range s {
if c == 'x' {
n++
}
}
return n, true
}

var n = flags.IntValue("n", 999, "count of something")
var m = flags.IntValue("m", n, "count of other thing")
var includes = flags.Value("I", []string{}, "includes").([]string)
var xs = flags. Value("x", Xs(0), "xs").(Xs)

var buf = make([]byte, n)

func main() {
flags.Check()
fmt.Printf("n %v; includes %v; xs %v; m %v\n", n, includes, xs, m)
fmt.Printf("args: %v\n", flags.Args)
}

flags.go

Rob 'Commander' Pike

unread,
Mar 29, 2010, 8:03:10 PM3/29/10
to golang-nuts Nuts
You talked me into it, especially since it was no code. Really.
Everything necessary was already there.

http://codereview.appspot.com/842041

-rob

Reply all
Reply to author
Forward
0 new messages