Solved Where should "help" output go?

zirias@

Developer
I have a very simple question: For a tool that offers verbose help (using -h or --help flags), should this help go to stdout or stderr?

There's of course a bit of background, I'm currently developing some "generic" configuration helpers for my "poser" project, still working on parsing configuration from the command line, and once this is finished, I plan to add support for configuration files (and both parsers should be stackable).

I already added a few convenience features, the lib can already auto-generate both "usage" and "help" messages from the configuration description. There are functions to write them to any FILE * the user wants. I also added a short-hand convenience function to add a "standard" flag that will automatically print the help message. So far, this automatic flag will print it to stderr.

Finally, I added another feature I kind of liked when I've seen it used by git: Generated output, only if it goes to an actual terminal, will be piped to $PAGER instead if it wouldn't fit into the current dimensions of that terminal (as an optional feature, must be explicitly enabled by the caller).

Now, this leads to an interesting surprise for the user. If output would go to stdout, all that would be needed to disable this auto-paging would be e.g. | cat. But as help output goes to stderr right now, to disable the auto-paging in a similar way, you'd have to e.g. write 2>&1 | cat instead.

Therefore, I now wonder whether help output should better go to stdout by default? For the short "usage" output, it's a different thing, as this is typically triggered by parsing/validation errors, so you actually have some error condition. But to get the help output, you explicitly write some flag to get it. So, would it make more sense to direct it to stdout instead by default?
 
stdout makes sense to me. Do you have examples of common programs that send it to stderr instead?
Not right off my head and I didn't do much testing in that regard ? Maybe I'm overthinking that and should just use stdout.

So far, I had this mindset that the two different output streams should be used to separate actual output of the tool in normal operation from "meta" output... and in this sense, a help message would be the latter.

But I guess yuripv79 is right, it's explicitly requested by the user. Thanks!
 
Dealers choice, but I've always used stdout for such things and reserve stderr for real error reporting.

My feeling is that for a well designed command line program, the -h or --help option should be the only supplied parameter so it's pretty obvious the output in that case. I'm a big fan of defaulting to stdout and making heavy use of pipes for processing output.
 
My feeling is that for a well designed command line program, the -h or --help option should be the only supplied parameter
I'm not sure that's really necessary, after all, it just complicates the "usage" scheme and doesn't provide any real benefit to the user. I could certainly implement it (just add a few more checks), but I'm kind of happy with my current model, where this kind of flag just has a special "action" type, stopping any parsing and immediately executing the requested action.

But anyways, thanks everyone for the quick replies, you convinced me some output actively requested by the user should go to stdout ?

Edit: and fixed ...
 
Followup question:

I think this (optional) auto-paging feature is quite nice, IMHO it's most often what the user actually wants when output goes directly to the controlling terminal, and after all, there's a reason $PAGER exists :cool: ... that's why I implemented it in the first place (and also, to be honest, as an exercise for myself to do it correctly ... no, the crappy popen(3) won't fit the bill here ...)

But after this short discussion here: Should I better restrict it to cases where the output indeed goes to stdout (as long as it's the terminal of course), instead of any FILE * that happens to point to the terminal? My "gut feeling" now is the answer should be "yes". :oops:
 
I'm not sure that's really necessary, after all, it just complicates the "usage" scheme and doesn't provide any real benefit to the user. I could certainly implement it (just add a few more checks), but I'm kind of happy with my current model, where this kind of flag just has a special "action" type, stopping any parsing and immediately executing the requested action.

But anyways, thanks everyone for the quick replies, you convinced me some output actively requested by the user should go to stdout ?

Edit: and fixed ...
You misunderstand my point. You'll generally not encounter -h on a command line with other params...with rare exceptions where the dev allows help for suboptions like under qemu.
 
Followup question:

I think this (optional) auto-paging feature is quite nice, IMHO it's most often what the user actually wants when output goes directly to the controlling terminal, and after all, there's a reason $PAGER exists :cool:

... that's why I implemented it in the first place (and also, to be honest, as an exercise for myself to do it correctly ... no, the crappy popen(3) won't fit the bill here ...)

So make your paged output be the optional extra switch, maybe "-hp"?

Second-guessing user into $PAGER breaks POLA, where user would add " | less" if desired anyway. Small, discrete, chainable cmdline processes is unix' strong point (IMO, OC)
 
I think this (optional) auto-paging feature is quite nice,
Without knowing about the application, it's hard to make the call.

Auto-paging may violate the software tools and KISS principles that underpin the fundamental design philosophy of Unix.

However, some applications really do benefit from it, e.g. command line mail readers (elm, pine, mutt).

Otherwise it's "creeping featurism". You get software bloat, for no good reason. Where ever possible, let the user choose which paginator they want... and let them pipe into it.

Edit: I just realised that I said pretty much the same as smithi...
 
Of course I'm gonna be the voice of dissent here...go figure...I would contend that the UNIX WAY is to stream the output to stdout and pipe it through more/less if you want paged output. It keeps the functionality in the programs where they are suppose to be.

Kind of like modern devs unilaterally believing that ANSI multi-colored output on text screens should be a default. Yuk!

Just my opinion, of which everyone is entitled. :p
 
First of all, I restricted the feature to only stdout now. Exactly because I came to the conclusion that nobody would ever expect stderr to get paged.

Then, it's interesting how many people obviously hate simple and small comfort features. Looking at git, it goes even a step further and "delegates" --help to man(1), and, surprise, man does exactly the same thing: use $PAGER (only) if the output goes to a terminal. (check man man | cat and see a man-page scroll by...). Of course, git also uses $PAGER (and even colors ?) for output it generates itself and, of course, again only if the output goes to a terminal ... which is exactly how $PAGER is meant to be used. But maybe you hate git as well ?

Anyways, I really can't agree with any of the points made here:
  • No, paging output going to the terminal does NOT violate POLA (see also above). It would be a violation if implemented incorrectly (the feature must go out of the way as soon as output goes anywhere else).
  • It doesn't interfere with users' choices at all. As always, you can set $PAGER as you like. And as we're talking about "one-shot" output here, just piping it to a different pager works as well.
  • So you really consider any feature that's not desperately needed "featurism"? Then we would have very little helpful comfort features. Of course, the consumer of the lib must explicitly enable auto-paging when desired. And then, it's only ever triggered when output 1) is on stdout, 2) goes to a terminal, 3) wouldn't fit the terminal size and 4) $PAGER is set to something non-empty. So, it just spares the user the typical sequence foo --help, "oh, that's too much text", foo --help | less.
  • Oh and for "bloat", sure, if you consider <2K added to some lib "bloat" ?‍♂️
 
Auto-paging may violate the software tools and KISS principles that underpin the fundamental design philosophy of Unix.

"SwTools => Tools not policies
Freedom with responsibility
System comes with compilers and development tools
Root-bit offers unhindered foot-shooting"

None more succinct than phk@
 
smithi, if you think there's any contradiction here, you'd have to explain it (and also blame man(1), after all, why doesn't it just format the page and leave piping to a pager to the user?) As mentioned above, man of course does NOT break the ability to pipe/redirect its output anywhere else, and neither does my feature here.

I think that's perfectly in line with the "philosophy". Output going to a terminal means presenting it to a user. Doing that well for "large" output means paging it. Attempting to do that "sub job" yourself would be stupid, so there's $PAGER and you just delegate that job to another tool.
 
smithi, if you think there's any contradiction here, you'd have to explain it

No contradiction, just different points of view, which is ok.

(and also blame man(1), after all, why doesn't it just format the page and leave piping to a pager to the user?)

No blame.

man's whole purpose is to format its documents for viewing and to emit them to stdout, via $PAGER if to a terminal (or $MANPAGER if defined, or some other pager with -P)

man -h issues an unpaged short usage summary. Note -P option, details of course in man man

As mentioned above, man of course does NOT break the ability to pipe/redirect its output anywhere else, and neither does my feature here.

Is your program of a similar nature to man, at all?

We've been talking generally about unix tools, and my previous response was as a nod to gpw928 for the phk@ link; always good to read a master craftsman's work.

I think that's perfectly in line with the "philosophy". Output going to a terminal means presenting it to a user. Doing that well for "large" output means paging it. Attempting to do that "sub job" yourself would be stupid, so there's $PAGER and you just delegate that job to another tool.

One example that I use often enough is sox -h, which emits 81 lines. I'd use then Shift-PgUp if need be in konsole, or ScrLk-PgUp in a pty.

OTOH, man sox | wc -l says 3543, it's big.

Peace, Ian
 
Is your program of a similar nature to man, at all?
This is about a library.

What I'm adding there right now is generic configuration parsers based on some description of the config structure. When the parser for command-line is enabled, a "help" message can be auto-generated from the same description. Depending on the actual description, this can be pretty large, but doesn't have to be. The library offers auto-paging for generated output as an option.

I will probably enable it for some of my tools. One of them currently emits a generated help message of 71 lines. It doesn't cost anything, it doesn't break anything (remember, pipes and other redirections still work exactly as expected), so why not.
 
I vote against paging. Mostly due to inertia I suppose in that *other* software doesn't do it. Therefore I would be "shocked" if your program unexpectedly did so. POLA has been violated. Conversely, it would be like making a man-page reader or a GNU info pages renderer that *doesn't* page. It would be weird.

Plus, the argument for -h to go to stdout is correct for another reason, you don't need to do weird stuff like:

Code:
$ prog -h 2>&1 | less

*Because* pipe'ing into a pager is something people do without thinking (another reason why the program shouldn't do it implicitly), you don't want to get in the way of that via sending to stderr.

Finally... "your pager will never be as good as my pager. Let me use mine!" Pipe'ing an auto paging tool into a pager can work, but again is weird as a concept.
 
I vote against paging. Mostly due to inertia I suppose in that *other* software doesn't do it. Therefore I would be "shocked" if your program unexpectedly did so. POLA has been violated.
I still don't agree. It might not be too common, but hey, git is doing it (just not for "help", it "delegates" this directly to man, which is even more uncommon...). If I had to guess, it's not uncommon because people wouldn't want it, but because doing it correctly is a bit non-trivial and there's no widespread library functionality for it :oops:

*Because* pipe'ing into a pager is something people do without thinking (another reason why the program shouldn't do it implicitly), you don't want to get in the way of that via sending to stderr.
This argument even worked in the opposite direction. With "auto-paging", having it on stderr lead to the very strange problem that a redirected stdout was inherited by the pager process, so to avoid showing nothing at all, it had to reopen the tty first. Yep, that was a really bad idea, and I got rid of that nonsense now.

Still about your parentheses: I really really don't get that. If a user pipes it to a pager, the "auto-paging" will never ever trigger, so why is this a concern at all?
 
This is about a library.

What I'm adding there right now is generic configuration parsers based on some description of the config structure.
When the parser for command-line is enabled, a "help" message can be auto-generated from the same description. Depending on the actual description, this can be pretty large, but doesn't have to be.
The library offers auto-paging for generated output as an option.

I like 'as an option', which throws design choice to the calling process.

I will probably enable it for some of my tools. One of them currently emits a generated help message of 71 lines.

Your tools, your choices. We may get to evaluate in time?

It doesn't cost anything, it doesn't break anything (remember, pipes and other redirections still work exactly as expected), so why not.

Fair enough; thanks for detail to make it clearer.
 
As a user, I prefer that -h or --help output be reasonably concise. As in: If the manpage is a mile long (like ifconfig(8)), then the -h or --help output should be something like:

Code:
$ prog -h
program manual: man program
homepage: https://program.sourceforge.org
Simple usage examples:
example 1
example 2

And if I were writing a program, I'd say that writing the help page's contents to stdout (as opposed to stderr) is a no-brainer.

If the idea is to do some interactive troubleshooting of nonsensical flag combinations (which does make the code complicated), then I'd consider going with stderr ?
 
As a user, I prefer that -h or --help output be reasonably concise. As in: If the manpage is a mile long (like ifconfig(8)), then the -h or --help output should be something like:
The thing here is: the tools I currently plan to move to this new feature don't even have man pages so far.

I guess I first have to explain my motivation behind the feature to auto-generate help from the same metadata also used to parse the config: I want to eliminate typical errors forgetting to update a specific part of documentation (or, updating it incorrectly) by establishing a "single source of truth".

Now, what the feature can currently do is generate a typical "usage" message (very short) and, well, the "full" help message. For anything in-between, that would probably need to be hand-written and, therefore, maintained... and the same holds for a man-page, at least I think attempting to auto-generate one would be off-limits in complexity.

I'm still pretty early in development, but did some testing with one of my tools, moving from this: https://github.com/Zirias/tlsc/blob/b30568cc4cb3b06cf13c8497fc89cccb3be6bd98/src/bin/tlsc/config.c to this: https://github.com/Zirias/tlsc/blob/1543f25c9b416a4bb045c44a71e31c42fe5585e9/src/bin/tlsc/config.c ... the auto-generated help now looks like this:
Code:
Usage: tlsc [-Vfhnv] [-g group] [-p pidfile] [-u user] tunspec [tunspec ...]

    -V, --version
        Print version information

    -f, --[no-]foreground
        Run in foreground, do not detach.

    -g group, --group=group
        Group name or id to run as. If a user is given, this defaults to its
        primary group.

    -h, --help
        Print this help text

    -n, --[no-]numeric
        Use numeric hosts only, do not attempt to resolve addresses to
        hostnames.

    -p pidfile, --pidfile=pidfile
        Use this pidfile instead of the default /var/run/tlsc.pid

    -u user, --user=user
        User name or id to run as.

    -v, --[no-]verbose
        Enable debug mode, will log debug messages.

    tunspec
        Description of a tunnel.

      Format: host:port:remotehost[:remoteport][:k=v[:...]]
      host
          Hostname or IP address to bind to and listen on.
      port
          Port to listen on.
      remotehost
          Remote hostname or IP address to forward to.
      remoteport
          Remote port to forward to, default: same as `port'.
      k=v: key-value pair, any of the following:
        C=[4|6]
            Only use IPv4 or IPv6 when connection as a client.
        N=[0|1]
            In client mode, enabling this option will accept any server
            certificate. Otherwise (default), the server certificate is
            validated agains the local store of trusted CAs.
        P=[4|6]
            Only use IPv4 or IPv6.
        S=[4|6]
            Only use IPv4 or IPv6 when listening as a server.
        b=hits
            A positive number enables blackisting specific socket addresses for
            `hits' connection attempts after failure to connect.
        c=cert
            Use a certificate file to present to the remote. When given, a key
            file is required as well.
        k=key
            The key file for the certificate. When given, a certificate must be
            configured as well.
        s=[0|1]
            Enable or disable server mode (default: disabled). In client mode,
            the forwarded connections use TLS. In server mode, incoming
            connections use TLS.
            When enabling server mode, a certificate is required.

    For sub-section arguments delimited by a colon (:), any colons and equals
    signs (=) contained in values must be escaped with a backslash (\).
    Alternatively, values may be quoted in a pair of brackets (between `[' and
    `]') if they don't contain those themselves.
 
I still don't agree. It might not be too common, but hey, git is doing it (just not for "help", it "delegates" this directly to man, which is even more uncommon...).
I think the difference is that the git help system is fairly substantial. I.e it has different categories and things like that. It is basically trying to be a manpage reader (arguably having decent manpages would be a better solution but GNU generally tries to avoid manpages and GNU info is... crap). It is getting into the same territory as :help in vim.

Still about your parentheses: I really really don't get that. If a user pipes it to a pager, the "auto-paging" will never ever trigger, so why is this a concern at all?
I suppose (IMO) I feel the concept of using two pagers is wrong. And for a split second, a user might have to think twice whether using their pager will conflict with the one provided by your program.

How does it interact with terminal emulators with a scrollbar?
 
I think the difference is that the git help system is fairly substantial. I.e it has different categories and things like that. It is basically trying to be a manpage reader (arguably having decent manpages would be a better solution but GNU generally tries to avoid manpages and GNU info is... crap). It is getting into the same territory as :help in vim.
Not really ... when given a --help flag, git just executes the actual "man" tool, e.g. git commit --help leads to these processes (edit: I really wonder why it involves the shell, this seems pretty pointless ....):
Code:
10440 10  Is   0:00,37 -zsh (zsh)
18166 10  I+   0:00,01 - /bin/sh /usr/bin/man git-commit
18177 10  I+   0:00,00 `-- less
other output git can generate (like e.g. logs, diffs, ...) are "auto-paged" by git itself.
I suppose (IMO) I feel the concept of using two pagers is wrong. And for a split second, a user might have to think twice whether using their pager will conflict with the one provided by your program.
Two pagers? ? This can never happen, as already explained, a pager is only ever forked for output that would go directly to the terminal, just like man and git (and any other sane tool offering such a feature) do it as well. I don't see how anyone could ever think this could happen?
How does it interact with terminal emulators with a scrollbar?
Not at all. Or, IOW, exactly the same as if you'd manually pipe the output to e.g. less. Scrollbars on terminal emulators are after all just GUI gimmicks for something every terminal emulator offers: a scroll-back buffer. They're not relevant for the size the terminal reports, that's always the immediately visible area.
 
This is all about usability and comfort, and thinking about that, I just fixed another very common issue. Most config parsers stop as soon as an error is encountered, and so did mine. And in fact, "fail fast" is often the best you can do, but not when parsing/validating stuff written by a user. In that case, it's much more helpful to give as many error messages as possible in a single run. Well, it seems to work (and wasn't too hard after all) :cool::
Code:
$ tlsc -g frob -u floo host:lalala:bar:x15:r=0:s=17
Usage: tlsc [-Vfhnv] [-g group] [-p pidfile] [-u user] tunspec [tunspec ...]

Unknown group: frob
Unknown user: floo
Argument `lalala' for key `port' is not a valid integer
Argument `x15' for key `remoteport' is not a valid integer
subsection: unknown key `r'
subsection: invalid boolean value `17'
 
Not really ... when given a --help flag, git just executes the actual "man" tool, e.g. git commit --help leads to these processes (edit: I really wonder why it involves the shell, this seems pretty pointless ....):
Whilst I agree that if help output is so long, it should be split up into a manpage like Git has done, it is fairly non-standard to open up a manpage from the program itself. Git is weird. Can you think of a tool from base UNIX that does similar?

Not at all. Or, IOW, exactly the same as if you'd manually pipe the output to e.g. less. Scrollbars on terminal emulators are after all just GUI gimmicks for something every terminal emulator offers: a scroll-back buffer.
I use the one on tmux a lot for scrollback from compile errors but indeed it can't beat a decent pager. Particularly where search is concerned.

But if you want to include the paging in your program, then really the only other argument I have is on Windows, i.e dir /? auto pages the output and it is a general rule that good software should do the opposite of what Windows does. :)
 
Back
Top