mitchellh / cli

A Go library for implementing command-line interfaces.
Mozilla Public License 2.0
1.73k stars 123 forks source link

Add support for sending expected output to stdout, and errors to stderr #81

Closed dnephin closed 4 years ago

dnephin commented 4 years ago

Problem

I encountered this problem when trying to view the Consul help with consul agent --help. Any time a command has a lot of help text I send it to a pager (ex: consul agent --help | less). I will also quite frequently pipe the help output to grep to quickly find the help text for a flag that I know exists, but I may not remember exactly what it expects as a value.

To my surprise, this did not work! I did some digging and found that all help text (and --version output), even when requested by the user, is always sent to stderr.

I looked to see how other users of this library have handled the problem and I found that nomad sets HelpWriter to os.Stdout. This fixes the problem, but introduces a new problem. If a user runs a command with an invalid flag the error message ends up on stdout instead of stderr. This can be very confusing to users when they are trying to run a command with output piped somewhere else, because the error will be hidden.

I believe it is best practice for a CLI to write requested help and version output to stdout, but to send that same output to stderr when it is being printed as a result of an invalid flag or subcommand.

$ git --help | wc -l
45
$ cat --help | wc -l
25
$ git bogus 2> /dev/null
$ git branch --bogus 2> /dev/null
$ cat --bogus 2> /dev/null

Solution

This PR addresses the problem by introducing a second Writer. I believe it is backwards compatible as the new writer is initialized to the value of the existing Writer.

I've seen this problem solved this way in another cli library so I thought it may be applicable here as well.