[HN Gopher] argp: GNU-style command line argument parser for Go
___________________________________________________________________
argp: GNU-style command line argument parser for Go
Author : networked
Score : 134 points
Date : 2025-03-23 12:45 UTC (10 hours ago)
(HTM) web link (github.com)
(TXT) w3m dump (github.com)
| arccy wrote:
| > * multiple options can be combined: -abc is the same as -a -b
| -c
|
| > * option values can be separated by a space, equal sign, or
| nothing: -a1 -a=1 -a 1 are all equal
|
| > * options can have multiple values: -a 1 2 3 means that a is an
| array/slice/struct of three numbers of value [1,2,3]
|
| while convenient, one of the reasons I dislike gnu style flags is
| just how inconsistent they can be and annoyingly hard to parse by
| humans
| nine_k wrote:
| Understandable! This is because `-a 1 2 3` assiging a = [1, 2,
| 3] is not GNU style.
|
| If you want that, the GNU style could be `-a "1 2 3"`.
|
| Normally `-a 1 2 3` would be a = 1, and 2 and 3 being
| positional arguments. A clarified way to write the same would
| be `-a 1 -- 2 3`.
| acmj wrote:
| In general, command-line argument parsers should just follow the
| GNU style. No more, no less. Deviations confuse users as it is
| not immediately obvious to them what rules a parser is imposing.
|
| > _options can have multiple values: -a 1 2 3 means that a is an
| array /slice/struct of three numbers of value [1,2,3]_
|
| Allowing multiple values is inconsistent because you can't tell
| in "./cmd -a 1 2 3" whether 2 and 3 are positional arguments or
| arguments for -a. This is not a GNU style. The GNU way is "./cmd
| -a 1 -a 2 -a 3" (or "./cmd -a 1,2,3"). This package supports
| that, which is good.
|
| > _option values can be separated by a space, equal sign, or
| nothing: -a1 -a=1 -a 1 are all equal_
|
| "--a=1" is a GNU style but "-a=1" is not. This is a minor issue,
| though.
|
| Also, does this package support "--"? Everything following "--"
| should be treated as positional arguments.
| cratermoon wrote:
| Disagree. Go's style may not be GNU style, but it's consistent
| in the Go community. GNU style is not All That and a Bag of
| Chips.
| hnlmorg wrote:
| As a heavy user of Go tooling, I still really dislike Gos
| style.
| bborud wrote:
| Agree. It was an unnecessary thing to do.
| acmj wrote:
| When I use a command-line tool, I don't know and don't care
| if it is written in Go or not. I just want to use it the same
| way as most of the other traditional Unix tools. The Go style
| gets in the way. We would have much more consistent CLI
| between tools if Go had just followed the GNU style at the
| beginning. The same can be said to many other languages like
| Nim that want to reinvent command-line argument parsing with
| their standard libraries. https://xkcd.com/927/ came to mind.
| klodolph wrote:
| Can't you just use --long options everywhere?
|
| For, like, 95% of the tools out there, I'm going to use
| --long every single time.
|
| Anyway, I don't even know the options that a program takes
| until I read the program docs. The program docs will tell
| me that the option is -host=localhost or --host=localhost
| or --bind-addr=localhost:8000.
|
| The other 5% are tools like ls, cp, mv. As far as I care,
| ancient tools are the only ones permitted to have short
| options that combine into a single option, like old-school
| getopt. Maybe a few exceptions now and then.
| treyd wrote:
| You can't expect the user to remember upfront what language
| the tool they're using was written in and context switch the
| style they're expressing their intents in based on that. The
| Go community has consistently shown it's not very good at
| making well-thought out decisions. GNU is obviously the
| better approach here.
| kirici wrote:
| > The Go community has consistently shown it's not very
| good at making well-thought out decisions.
|
| Please, do name one language that has.
| arp242 wrote:
| > it's consistent in the Go community.
|
| Except it's not? There are tons of flag packages in real use,
| because the stdlib one kind of sucks. Even the "go" tool
| itself works around some of its limitations (things like "go
| test ./... -v" won't work out of the box, since it will stop
| parsing flags at the first non-flag, so the go command
| reorders os.Args before sending it off to the flag package).
|
| Things like "-a 1 2 3" are not standard "Go style" at all.
| I've never seen that.
| nloomans wrote:
| I disagree with it being a minor issue. If I write a shell
| script around a program that accepts GNU-style arguments, I
| expect the following to be correct: ./cmd
| -a"$USER_CONTROLLED_DATA"
|
| A program using this package would break that assumption,
| introducing a bug where this user-controlled data cannot start
| with an '='.
| duckerude wrote:
| I researched this for my own argument parser
| (https://github.com/blyxxyz/lexopt/issues/13) and concluded
| that it's a minor issue.
|
| This syntax is supported by argparse and clap, the most
| popular argument parsers for Python and Rust respectively,
| and it seems to have caused almost no problems for them. It's
| a problem for the uutils implementation of cut, since `cut
| -d=` is common, but that's the only instance I could find
| after a long time scouring search engines and bug trackers
| and asking for examples.
|
| If anyone does know of other examples or other places this
| has been discussed I'd love to hear it though, maybe I just
| haven't found them.
|
| (Also, the more reliable way to write this in general is `-a
| "$USER_CONTROLLED_DATA"`, since that'll behave correctly if
| $USER_CONTROLLED_DATA is empty. As will
| `-a="$USER_CONTROLLED_DATA"` if you know the command supports
| it.)
| cb321 wrote:
| Ha! I just said that. :-)
|
| Anyway, one other alternative for the `cut` situation is to
| allow either ':' or '=' to optionally separate the key and
| the value. Then you can say `cut -d:=` or `cut -d=:` if you
| wanted to use either one. This is what
| https://github.com/c-blake/cligen does (for Nim, not Go).
| duckerude wrote:
| The problem is existing shell scripts and muscle memory
| and command histories. `cut -d=` has always worked and
| works on all the other implementations so it should keep
| working if you switch to uutils.
| cb321 wrote:
| Literally any short option key that takes a string or a
| char that could legitimately start with '=' has the
| problem, though, not just `cut`. The '=' will be "eaten"
| by one tool and left in another. But you know that. You
| write a bit as if we disagree, but I don't see any real
| point of contention. :-) Also, uutils has a very strict
| "drop-in" agenda. So, as another e.g., if you want `cp
| -t=foo` or `cp -S=foo` to work the same, you're going to
| have trouble if that '=' is eaten.
|
| So, in this case that would seem to imply a problem for
| any utilities with options taking strings-or-chars, not
| merely `cut -d`. If uutils really wants to be strictly
| drop-in compatible, they may well need to roll their own
| option parser or twist the arm of whoever's they use to
| provide a mode for them.
|
| In the more general case, "cross compatibility" may just
| always be limited by the reality that people just
| disagree on this stuff "more than they seem to _think_
| they do " (at least in my experience) and definitely more
| than they wish they did. I surveyed my /usr/bin once and
| like half of thousands of commands did not work with
| --help (yes, running that took some confidence in
| backups! but anyone could replicate on a throwaway VM or
| something). Consistency is nice, but consistency with
| what? -l=foo is consistent with --long=foo, but not
| (some, but not other) historical things.
|
| I'm not sure there will ever will be a world in which you
| don't need to know which PLang/CL toolkit was used to
| make a CLI utility if you _really_ want to know its
| syntax. The article 's lib is going its own way from the
| Go stdlib. POSIX is pretty darn calcified. A 15 year old
| Python stdlib thing is unlikely to ever change in this
| regard. Python also allows "--beg" for "--beginning-long-
| option" if nothing else starts with "--beg" even back in
| its optparse days and that also tends to be
| controversial. cligen tools actually provide a --help-
| syntax. Maybe something like that could take off?
| duckerude wrote:
| I can think of a lot of cases where it theoretically
| _could_ be a problem, but `cut -d=` is the only one I 've
| found so far where an end user ran into trouble because
| of this ambiguity, and I think it's the only one for
| which uutils bothers implementing a workaround. That's
| why I give it special attention.
|
| > You write a bit as if we disagree, but I don't see any
| real point of contention. :-)
|
| The `cut -d:=` spelling solves a different problem than
| the one I meant (and the one you're now talking about).
| But we're mostly on the same page!
| ucarion wrote:
| I think short options taking a value in the same argv (i.e.
| `-o=1` stuff) isn't a GNUism mostly because it's backwards-
| incompatible with POSIX. `=` is a valid getopt option
| character, `chmod` uses it.
|
| That said, I think? 'nloomans means for
| USER_CONTROLLED_DATA to be a set of short flags, not flag
| values, as in: root@08e9950d5bfd:/#
| export USER_CONTROLLED_DATA=lh
| root@08e9950d5bfd:/# ls -a"$USER_CONTROLLED_DATA"
| total 56K drwxr-xr-x 1 root root 4.0K Mar 23
| 16:51 . drwxr-xr-x 1 root root 4.0K Mar 23 16:51
| .. [...]
|
| Not that I've seen this in the wild before. But everyone's
| use of bash is a unique personal hell anyway.
| edoceo wrote:
| In the Gentoo world, sometimes you need to give an exact
| package name which looks like `=net-misc/foo-0.1.2-r1`. The
| exact match has to start with the '='.
| cb321 wrote:
| It is safer to just put in the space (much like you put in
| the quotes to be safe). Python's argparse will also accept
| but not require an `=` separator (maybe optparse, too - I
| haven't checked that one).
| Arch-TK wrote:
| Unfortunately a lot of people nowadays don't seem to notice or
| care and you even get weird arguments like the one about "Go
| style" vs "GNU style" below. I've also heard arguments about
| making it more "user friendly" (at the cost of any power user
| wanting to re-write your tool).
|
| I wouldn't mind if GNU style was consistently extended to allow
| annotated required arguments. But that's about it.
|
| That being said. As a rule, if your command line utility has
| enough positional arguments that you're forgetting which one is
| which, it's a badly designed command line interface. Most often
| it's because you're doing too much, not settling on sensible
| defaults for things which you shouldn't need to specify every
| time, or just doing something outright weird.
| mjevans wrote:
| IME commands get there by expressing complexity that belongs
| in a full configuration file on the command line (as well /
| instead).
|
| It's nice if things are simple enough that a hand-full or two
| of flags are sufficient. However more complex programs that
| do more or substantially different things from traditional
| bytestream filter programs often do need proper configuration
| files.
| marxisttemp wrote:
| No, command tools should follow the POSIX standards, not GNU.
| masklinn wrote:
| You're almost right. No command tools should follow the POSIX
| standard. A standard which all but forbids long options
| deserves only scorn.
| NekkoDroid wrote:
| POSIX doesn't even provide any utilities to write long opts
| and it also doesn't even define any long opts for basically
| any of its commands, so by default it is barely usable in
| maintainable scripts.
| greyw wrote:
| POSIX doesnt even define long opts. I conciously dont follow
| POSIX these days. The standard will adapt once enough
| pressure has built up as they mostly document existing things
| instead of innovate. So I need to apply pressure.
| ants_everywhere wrote:
| Are you saying this because you think the GNU standard is
| particularly well thought out, or because it's so common that
| deviations are confusing in general?
|
| I'm so used to the GNU conventions that I'm not really aware of
| what the alternatives are or what their merits are.
| gjvc wrote:
| GNU became the de-facto standard at least 20 years ago
| acmj wrote:
| The most important part is to have a standard, which doesn't
| need to be perfect. On argument parsing, I actually think the
| GNU way is the best so far. I have seen various deviations
| from GNU and I _personally_ regard all of them inferior.
| amelius wrote:
| Does it throw an exception when it fails to parse?
| johnisgood wrote:
| According to the example and judging by the code at a quick
| glance, it does not, it seems to exit, which is unfortunate.
| Perhaps the author may correct me.
| latchkey wrote:
| Cobra https://github.com/spf13/cobra uses
| https://github.com/spf13/pflag, which supports GNU style flags.
| Cobra has been developed for many years now and has a ton of
| additional features, like automatically generating the
| autocompletions and has tons of unit tests.
|
| I'd just use cobra.
| imiric wrote:
| Cobra is certainly popular, but it has many weird edge cases
| and limitations. It's been a while since I've used it so I
| can't recall the specifics, but I do remember it being a very
| poor experience, both as a developer and as a user.
|
| I've had a much better experience with
| https://github.com/alecthomas/kong , and I've heard good things
| about https://github.com/urfave/cli , so I would recommend
| those over Cobra.
| latchkey wrote:
| Kong doesn't explicitly say it supports GNU-style, which is
| why I didn't mention it.
| peterldowns wrote:
| They all have their warts, but "just pick cobra/kong/cli" is
| what I go with these days. I'm still partial to Cobra because
| of the nice autocompletion scripts it generates for me so I
| get tab-completion for free.
| MrDarcy wrote:
| This is the correct answer. Just use Cobra, the automatic
| autocomplete is the best UX for users.
|
| I've also tried all of them. cobra is more than good enough
| and what issues remain in 2025 are easily and reliably
| worked around to be non issues.
| networked wrote:
| I have used both argp and Kong. I consider them two of the
| best Go CLI libraries for different reasons. Kong has a full
| set of advanced functionality like flag groups and
| associating environment variables with options. It is highly
| useful in a larger project with a complex CLI. For a simple
| command-line interface, I prefer argp's small API and
| defaults, like exit with status 2 on bad usage. (Disclosure:
| I contributed this default to argp.)
|
| Kong 1.7 has a disadvantage when it comes to negative
| numbers. argp parses `foo --bar -1` as `foo --bar=-1` the way
| you expect when `--bar` takes a value, but Kong treats `-1`
| as a flag.
|
| Edit: Expanded the first paragraph.
| acmj wrote:
| > _argp parses `foo --bar -1` as `foo --bar=-1` the way you
| expect when `--bar` takes an value, but Kong treats `-1` as
| a flag._
|
| This seems a bug in Kong. Has someone reported it?
| networked wrote:
| Yes, see https://github.com/alecthomas/kong/issues/315.
| It may be nontrivial to change.
| donatj wrote:
| Yes! People love Cobra and yet every time I've used it, it's
| felt super janky and unreliable at actually doing what you
| think it should.
|
| Not a fan.
| programd wrote:
| I'll second that. Cobra is the industrial strength flag parsing
| package most of the professional Go ecosystem has settled on.
| It's the boring choice in the best sense of the word. Use it
| and move on with life.
| hujun wrote:
| one thing I liked about cobra is the auto complete support, but
| I don't like the manual process of define command and parameter
| and not easy to extend, so I wrote https://github.com/hujun-
| open/myflags, it is built-on top of cobra but allow uses a
| struct to define all commands/args and also easily to extend
| support new types (even existing types)
| jmclnx wrote:
| If you need long options because you are running out of letters,
| time for a rewrite :)
| dotemacs wrote:
| I'm glad that this package exists.
|
| I know of UNIX parameters, with a single hyphen & a single
| letter, and GNU parameters, with a double parameter and a word.
| But a single hyphen and then a word: *mind broken*.
|
| When I first saw that on Terraform, I was upset & conflicted.
|
| OK, I know that Java also has mental parameter options like that
| ('-version', note the *single* hyphen followed by, gasp, a
| word!), but I just dismissed it as weird enterprise-ism.
|
| Then I saw that this 'single hyphen + word' was an accepted
| convention for parameters, in the Go world & I was disappointed.
|
| Glad that this package is fighting the good fight.
| PhilipRoman wrote:
| `find` is also notorious for its parameter style. But in my
| opinion programmers sometimes put _too_ much importance on
| following conventions. You 're not gonna go blind from looking
| at an incorrectly indented line. If the founding fathers of
| unix didn't want us creating horrifying monstrosities, they
| shouldn't have given us unlimited access to argv :)
| bqmjjx0kac wrote:
| Fun fact, `find`'s wonky argument syntax predates GNU syntax.
| It was part of Version 5 Unix from 1974.
| https://en.wikipedia.org/wiki/Find_(Unix)
| orthoxerox wrote:
| As was `dd`, another program with an unorthodox syntax.
| darrenf wrote:
| > _OK, I know that Java also has mental parameter options like
| that ( '-version', note the *single* hyphen followed by, gasp,
| a word!), but I just dismissed it as weird enterprise-ism._
|
| I think "mental" is being too kind about java.
| $ java -version
|
| and $ java --version
|
| both work, their outputs are different, and `-version` goes to
| STDERR whereas `--version` goes to STDOUT.
| edoceo wrote:
| I use Kong https://github.com/alecthomas/kong
|
| I does everything, including too complicated options.
| d_burfoot wrote:
| I might be the only person in this camp, but I find the
| "standard" command line arguments style absolutely repulsive. I
| write tons of CL code, and I always use easy key=value notation
| (sometimes it's flag=true, which I consider to be a minor
| sacrifice of conciseness in favor of consistency and
| readability).
| bborud wrote:
| It is more important to follow a common convention than agree
| on which looks better. If you are lucky you get used to common
| conventions and spend no time annoyed with them. If you are
| unlucky you will struggle to let it go. Struggling to let go is
| a disadvantage.
|
| People who ignore convention and insist on whatever they like
| are never any fun to work with.
| sandreas wrote:
| Interesting. I used
|
| https://github.com/urfave/cli
|
| in the past and was pretty happy with it. However sometimes it's
| good to try something New :-)
| thrill wrote:
| Sanity!
| CamouflagedKiwi wrote:
| I've become a fan of https://github.com/jessevdk/go-flags (also
| enjoyed structopt which is similar for Rust). I much prefer
| defining the flags statically on a type which stores them after
| rather than building it all up programmatically.
|
| Otherwise, I'm not really sure what this offers that alternatives
| don't, apart from the -a 1 2 3 -> [1,2,3] business which seems
| highly confusing and undesirable to me so I'd think of it as more
| of an anti-feature.
| mway wrote:
| Also a fan of go-flags, I use it by default most of the time
| (unless the use case is so basic that the stdlib flags usage
| would be sufficiently simple).
|
| I think my only real point of "frustration" (mild at best) is
| that flag composability isn't always possible (eg
| embedding/sharing common non-root flags across flag structs),
| IIRC struct tag eval doesn't reach nested types.
|
| Other than that though, it's one of those nice libraries that
| just does a thing and gets out of your way.
| CamouflagedKiwi wrote:
| Hmmmm I thought that sharing a common type did work in the
| way you would expect it to? I haven't tried embedding with it
| though I don't think.
|
| TBH I rarely do it much anyway, mostly the flag struct ends
| up being one anonymous struct which is operated on in the
| main package only. I do like writing custom types for parsing
| though - I've seen engineers do a bunch of post-hoc parsing
| logic which generally just ends up being messy.
| reactordev wrote:
| Aside from the other comments, an ick I have is that struct tags
| are being abused here. Instead of providing a grouping like json,
| yaml, mapstructure, gorm, etc - it just goes willy nilly with
| struct tags like it owns the whole space. I'd like to see it
| grouped like... type Conf struct {
| Thing string `argp:"name:thing;short:t;desc:A thing you can do"`
| }
|
| This would follow go struct tag standards.
| braggerxyz wrote:
| I really favor posix style, which is why I use pflags package for
| my cli Tools.
| odc wrote:
| The standard flag package in Go also supports GNU style flags.
|
| https://pkg.go.dev/flag#hdr-Command_line_flag_syntax
| declan_roberts wrote:
| Assigning values like -v as an int and allowing -vvvv to be
| easily parsed is a great compatibility feature for existing
| utils.
| synergy20 wrote:
| i hope the below is agreed on, mycmd -h, and mycmd --help, are
| the same, - for single letter,-- for longer format
|
| same to mycmd -v, mycmd --version
___________________________________________________________________
(page generated 2025-03-23 23:00 UTC)