-
Notifications
You must be signed in to change notification settings - Fork 17.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
flag: exit 0 for default -h/-help option #37533
Comments
Change https://golang.org/cl/221427 mentions this issue: |
If a program is run with an undefined flag, that's an error, because it's being invoked incorrectly. Unless we report this as a non-zero error status, the error will never be detected. On the other hand, if you explicitly ask for help, using a defined |
@bitfield , reading your second paragraph, I think there is a misunderstanding. What I mean is that if the flags The Maybe if you run |
No, we understand one another correctly, I think. We agree that if the program defines the flag However, if the program does not define |
/cc @robpike |
The problem here is that although we recognize -h and -help in flag, all that it does is avoid printing "unrecognized flag: -help". Then it calls f.usage, which is user-defined. And essentially all the usage functions in existence do os.Exit(2). I guess those would keep exiting 2. The main flag.Parse loop would see an ErrHelp and I suppose it could treat that case, provided the error handling is set to ExitOnError, as os.Exit(0) instead of os.Exit(2). It's a bit of a corner case. Do all the other getopt/argparse/etc agree to exit 0 on -help, or is there variation? |
I don't think it's a good idea to have specific behaviors for Ignoring the point above, what is the merit, or usefulness, of homogeneously returning 0 when |
@as , this proposal does not propose any changes to go programs that have the This proposal is useful for programs, which do not have the |
@rsc, yes, it already is a corner case before this proposal. flag.go has special handling for Looking at argparse for python3 and getopt for GNU C, and both of them exit 0 when programs don't explicitly defined a help flag. GNU C argp
#include <stdlib.h>
#include <argp.h>
const char *argp_program_version =
"argp-ex2 1.0";
const char *argp_program_bug_address =
"<bug-gnu-utils@gnu.org>";
static char doc[] =
"Argp example #2 -- a pretty minimal program using argp";
static struct argp argp = { 0, 0, 0, doc };
int
main (int argc, char **argv)
{
argp_parse (&argp, argc, argv, 0, 0, 0);
exit (0);
} Produces: $ ./a.out --help
Usage: a.out [OPTION...]
Argp example #2 -- a pretty minimal program using argp
-?, --help Give this help list
--usage Give a short usage message
-V, --version Print program version
Report bugs to <bug-gnu-utils@gnu.org>.
$ echo $?
0 python3 argparse
import argparse
parser = argparse.ArgumentParser(description='Process some integers.')
parser.add_argument('integers', metavar='N', type=int, nargs='+',
help='an integer for the accumulator')
parser.add_argument('--sum', dest='accumulate', action='store_const',
const=sum, default=max,
help='sum the integers (default: find the max)')
args = parser.parse_args()
print(args.accumulate(args.integers)) Produces: $ ./flag.py -h
usage: flag.py [-h] [--sum] N [N ...]
Process some integers.
positional arguments:
N an integer for the accumulator
optional arguments:
-h, --help show this help message and exit
--sum sum the integers (default: find the max)
$ echo $?
0
|
The exit code of usage is depend on whether the "help" or "-h" is defined for the application.
argp or argparse provide "-h" or "--help" automatically. If you want to get exit code 0 with -help, you should define "-h" your self, i think. |
@sding3, thanks for checking GNU C argp and Python argparse. @ianlancetaylor says that GNU getopt also exits 0 for help. The only one that I'd want to check that's left is BSD getopt, but really it doesn't have any concept of -h or --help at all. I guess most BSD getopt-using programs just treat -h the same as any other unrecognized option. For example, on a Mac:
I guess if we're going to go to the trouble of implementing -h and --help as a special case, it would make sense to match all the others. Does anyone see any argument for not changing the exit status of these to match all other languages we have checked? Thanks. |
Based on the discussion above and the lack of objections raised since my last comment, this seems like a likely accept. |
No change in consensus, so accepted. |
Change https://golang.org/cl/227178 mentions this issue: |
@ret394 Dropping the handling of That said, note that I don't think these options impose anything on anyone. Any program can define |
Too bad I only see this now, I tripped over it when reading the Go 1.15 release notes. To me it seems you just changed the command-line API of all Go programs using the flag package with the default set of command-line flags in subtle ways (given that To recreate old behavior one would now have to switch to a custom flag-set with For me that breaks the Go 1 Compatibility Expectations. It wasn't a security issue, unspecified behavior, a specification error, or a bug. It was clearly specified behavior of the standard library that now has been changed in subtle ways for, in my opinion, no good reason. If one wanted the new behavior before it was always possible with the means of the For me one of the biggest advantages of Go is that it doesn't change all the time breaking things that used to work before. Introducing subtle changes is even worse than breaking things, because it's so hard to detect. Like I said, this changes the CLI API of a lot of Go programs out there in the wild. I would even go so far to say this should be flagged as a bug and be reverted for Go 1.15.1. |
@frankbraun Can you show an example where this new behavior will cause a problem? The flag package never really defined the behavior of |
The default behavior is
And: ErrHelp is the error returned if the -help or -h flag is invoked but no such flag is defined.
So before the change the behavior was
and now it's:
Never mind what's printed on stderr (and hasn't been specified). I'm talking about exit codes here which are part of the API of a CLI program. So let's say I have a CLI program (called
And a shell script
In Go 1.14 calling
And now in Go 1.15 calling
So suddenly my So not only did this change break the Go 1.0 Compatibility Expectations (by changing specified behaviour), it also silently changed the CLI API (in terms of error codes, never mind undefined stderr output) of a lot of Go programs. @ianlancetaylor Does that clarify it? |
I'm sorry, I should have been more clear. Can you show me an example of an existing program or script where this new behavior will cause a problem? |
No, I haven't seen anything in the wild yet, Go 1.15 was just released. Do you get my point though that this breaks existing CLI APIs and violates the Go 1.0 Compatibility Expectations? IMHO @rsc shouldn't have put it up for a "consensus discussion", it's an API change that's not cool given the compatibility promise. I know I'm a bit nit-picky here, but this really ticked me off. I have decommissioned all third party Go option parsers for exactly the reason that they keep changing and keep breaking in subtle ways (apart from the fact that the amount of dependencies some of these parsers pull in is just crazy). I would really like if unnecessary stdlib changes don't suddenly change the behavior of my programs. |
I do get your point that it changes the CLI behavior. It's not obvious to me that this violates https://golang.org/doc/go1compat. As I said above, the behavior of I agree that it's a grey area. But the point of https://golang.org/doc/go1compat is not that we can't change anything in the existing libraries. It's that we won't break existing programs. So for areas like this that seem to me to be grey, I think it's very relevant to ask for examples of programs that break. I'm sorry this ticked you off. |
Changes to exit codes will affect Makefiles and Docker builds, as a couple of trivial examples, whereas output on standard error won't. |
As I wrote in my second message it was clearly specified in the documentation of The stderr output difference for
You won't know if this breaks existing programs, it might be deeply buried in some shell script calls. In my opinion this sets a dangerous precedent. It also was the correct behavior in the first place: You call a binary with a non existing flag and then you get an error code. |
For me the whole point of https://golang.org/doc/go1compat is that I can just upgrade and things generally get better. Not that behavior changes.
It's a total waste of time if I have to comb through all release notes to check for subtle library behavior changes and then change existing code to stick to old behavior. It's a massive advantage of Go that things keep working as they did before and generally just get better and faster. There are very few modern programming languages where you can take code that was written 8 years ago and just compile and run it successfully with the latest compiler. |
Two small points.
Thanks! |
Thanks for the reply. I rest my case until I have more hard evidence. Hopefully I don't have to open a bug report ;) |
Since you are seeking hard evidence, I'll note that this did cause a test failure for a CLI tool I maintain. The test in question expected an exit status of 2 when passed the |
When the
-help
or-h
flags are undefined and invoked, theflag
package handles this situation as a special case and prints a nice and helpful default help text, but exit the process with exit code 2. This proposal proposes the exit code be 0 by default and configurable for this specific case.Emphasis: this proposal does not propose/incur any changes to programs which has
-h
or-help
defined.As a concrete example, using
gofmt
, which uses theflag
package and does not have-help
or-h
defined, the behavior today is:The proposed behavior is:
The text was updated successfully, but these errors were encountered: