question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Built in Help and Version output goes to stderr not stdout.

See original GitHub issue

The built in --help and --version outputs are printed to stderr instead of stdout, you have to redirect them to use the commands properly.

test --version returns the version number, but on stderr not stdout.

e.g. test --version 2>nul returns no output. test --version 2>&1 prints the version to stdout as expected.

Issue Analytics

  • State:open
  • Created 5 years ago
  • Comments:13 (2 by maintainers)

github_iconTop GitHub Comments

4reactions
oliverhollidaycommented, May 10, 2019

I dont think it’s reasonable that printing out version or help output when specifically requested is counted as an error. If help output is triggered by invalid parameters then absolutely.

The GNU guidelines say the same.

Azure DevOps pipelines count any write to stderr as a pipeline error, because why wouldnt it - it’s specifically signalling an error!

When I do an automated app installation I make it print the version output as a quick check for first run but using this library I have to redirect stderr to stdout, which means I can’t possibly verify whether it worked or whether it legitimately errored.

Printing information to a screen is only one of the uses for standard streams, there are other use-cases for applications other than running them manually in a terminal. It makes sense to respect the standards and behave like other applications - i.e. only using stderr for printing errors.

2reactions
twaalewijncommented, May 11, 2019

@moh-hassan Of course, I’d be happy to.

PR context

I’ll start with some context and the assumption that the current behavior is considered a bug because that is how the PR is implemented right now.

In the PR I’ve replaced the HelpWriter property’s TextWriter type with a new class called HelpWriter.

This HelpWriter class takes either one or two TextWriters to write help to. If only one TextWriter is passed all help gets written to that writer (which is similar how it currently works). If two TextWriters are passed, the results from ‘Errors’ that are caused by specifically entering help/version/--help/--version are written to the first writer and others to the second.

// All help written to Console.Error.
new HelpWriter(Console.Error);

// All help written to Console.Out.
new HelpWriter(Console.Out);

// help/version/--help/--version written to Console.Out and other errors to Console.Error.
new HelpWriter(Console.Out, Console.Error);

Next the ParserSettings of Parser.Default were changed to return a HelpWriter that behaves like the third example above:

// HelpWriter.Default currently lazily returns a new HelpWriter(Console.Out, Console.Error).
private static readonly Lazy<Parser> DefaultParser = new Lazy<Parser>(
    () => new Parser(new ParserSettings { HelpWriter = HelpWriter.Default }));

So the new standard would be that help/version/–help/–version results are written to Console.Out and other parsing errors still get written to Console.Error.

This is, as you rightfully pointed out, a breaking change for anyone using the default parser and expecting all Error class related output to go to Console.Error.

Current PR behavior

A developer that that wants to disable all help/error text can still continue using the old way to do so:

// Setting the HelpWriter to null still prevents any help from getting written
// so no change in this situation.
// This also means that developers who write their own completely custom help/error texts
// are probably unaffected since they would have had to disable auto help this way anyway.
Parser parser = new Parser(settings => settings.HelpWriter = null);

A developer that was intercepting auto generated help will probably have assigned the HelpWriter on ParserSettings to their own TextWriter instance.

StringWriter myStringWriter = new StringWriter();

// Developers assign their own TextWriter to manage auto written help themselves.
Parser parser = new Parser(settings => settings.HelpWriter = myStringWriter);

These developers will find out that their code no longer compiles after updating the package. This was done intentionally so they are notified of the changed behavior of being able to differentiate between help/version/–help/–version output and other parsing errors.

If they want to keep managing their help this way without any changes they can simply instantiate a HelpWriter with their current TextWriter.

StringWriter myStringWriter = new StringWriter();

// Developer still wants all help to be written to their own own TextWriter.
Parser parser = new Parser(settings => settings.HelpWriter = new HelpWriter(myStringWriter));

If they want to keep managing the help but differentiate between help/version output they can filter both streams by adding another TextWriter.

StringWriter helpVersionStringWriter = new StringWriter();
StringWriter errorStringWriter = new StringWriter();

// Developer wants to do something with the auto generated help
// but filter between help/version verb/option output and parsing error output.
Parser parser = new Parser(
    settings => settings.HelpWriter = new HelpWriter(helpVersionStringWriter, errorStringWriter));

Breaking change workaround

Now, if it were to be decided that this issue is in fact not a bug but something that was done by design and changing it would cause a breaking change that cannot be accepted without bumping the major version number to a 3, I could change the PR with what I suggested in my earlier comment.

This would stop the breaking change from occurring because the HelpWriter.Default that is used by the default Parser would still write everything to Console.Error:

// HelpWriter.Default would lazily return new HelpWriter(Console.Error).
// No effective behavior changes for developers that only use the default parser.
private static readonly Lazy<Parser> DefaultParser = new Lazy<Parser>(
    () => new Parser(new ParserSettings { HelpWriter = HelpWriter.Default }));

As a result, myself and others like @oliverholliday who want to count output that was triggered by calling the help/version verbs or --help/–version options specifically as output of a command (and thus something that needs to be written to Console.Out), would still be able to easily do so by assigning a HelpWriter that does this to the ParserSettings:

Parser parser = new Parser(
    settings => settings.HelpWriter = new HelpWriter(Console.Out, Console.Error));

I’d still prefer the behavior of how the PR is implemented now because personally I consider it a bug that specific calls to the help/version verbs and options were ever written to Console.Error.

Having said that I also agree that potentially breaking scripts that were counting on everything being written to Console.Error without proper warning could be undesirable. Which is arguably the case when using the default parser if a developer does not read the eventual changelog.

So I could live with setting settings.HelpWriter = new HelpWriter(Console.Out, Console.Error) for now as long as an eventual v3 of the library changes it to be the default again.

I think this covers all the use cases but if it did not and you have more questions please just ask.

Read more comments on GitHub >

github_iconTop Results From Across the Web

'app --help' should go to stdout or stderr?
Only errors go to stderr . This is in no way an error, it does exactly what the user had in mind, which...
Read more >
A program's output seems to be neither stdout nor stderr?
I got a program vsftpd which output doesn't seem to be either stdout or stderr (if that's even possible?) My terminal output is:...
Read more >
Determine if output is stdout or stderr
There are only three ways I know of to determine what a program will output to STDOUT and what to STDERR. Read the...
Read more >
How to redirect shell command output | Enable Sysadmin
Learn how to process the output of shell commands within a script and ... By default, STDOUT and STDERR are sent to your...
Read more >
Subprocess management — Python 3.11.4 documentation
Source code: Lib/subprocess.py The subprocess module allows you to spawn new processes, connect to their input/output/error pipes, and obtain their return ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found