commandline
commandline copied to clipboard
Why does using --help do this?
Out of interest now when I use --help I get the help listed twice:
C:\Program Files (x86)\Meeting Schedule Assistant\OutlookCalIFConsole>outlookcalifconsole --help
OutlookCalIFConsole 2018.4.4.1
Copyright c 2017 - 2018
-l, --list Builds a list of available calendars. Eg: -l ".\CalendarList.xml"
-m, --mode Type of events to add to the calendar. Modes: mwb. Eg: -m mwb
-p, --path Path to the events data file. Eg: -p ".\EventsToAdd.xml"
-t, --tokencache Required. The location of the token cache data file. Eg: -t "file.dat"
-e, --errorlog Required. The location for the error log. Eg: -e "<path to error folder>"
-s, --signout Sign out from Outlook calendar
--help Display this help screen.
--version Display version information.
OutlookCalIFConsole 2018.4.4.1
Copyright c 2017 - 2018
-l, --list Builds a list of available calendars. Eg: -l
".\CalendarList.xml"
-m, --mode Type of events to add to the calendar. Modes: mwb. Eg: -m
mwb
-p, --path Path to the events data file. Eg: -p ".\EventsToAdd.xml"
-t, --tokencache Required. The location of the token cache data file. Eg:
-t "file.dat"
-e, --errorlog Required. The location for the error log. Eg: -e "<path
to error folder>"
-s, --signout Sign out from Outlook calendar
--help Display this help screen.
--version Display version information.
Why might this be? If I use --version it is not listed twice.
I am using standard code to build the help:
static int Main(string[] args)
{
var parserResult = CommandLine.Parser.Default.ParseArguments<Options>(args);
parserResult.WithParsed<Options>(options => OnSuccessfulParse(options));
parserResult.WithNotParsed<Options>(errs =>
{
var helpText = HelpText.AutoBuild(parserResult, h =>
{
return HelpText.DefaultParsingErrorsHandler(parserResult, h);
}, e =>
{
return e;
});
Console.WriteLine(helpText);
ReturnErrorCode = ErrorCode.CommandLineArguments;
});
return (int)ReturnErrorCode;
}
Also, why doesn't the default help keyword have a short version of h?
Looking here:
/// <summary>
/// Creates a new instance of the <see cref="CommandLine.Text.HelpText"/> class,
/// automatically handling verbs or options scenario.
/// </summary>
/// <param name='parserResult'>The <see cref="CommandLine.ParserResult{T}"/> containing the instance that collected command line arguments parsed with <see cref="CommandLine.Parser"/> class.</param>
/// <param name="maxDisplayWidth">The maximum width of the display.</param>
/// <returns>
/// An instance of <see cref="CommandLine.Text.HelpText"/> class.
/// </returns>
/// <remarks>This feature is meant to be invoked automatically by the parser, setting the HelpWriter property
/// of <see cref="CommandLine.ParserSettings"/>.</remarks>
public static HelpText AutoBuild<T>(ParserResult<T> parserResult, int maxDisplayWidth = DefaultMaximumLength)
{
if (parserResult.Tag != ParserResultType.NotParsed)
throw new ArgumentException("Excepting NotParsed<T> type.", "parserResult");
var errors = ((NotParsed<T>)parserResult).Errors;
if (errors.Any(e => e.Tag == ErrorType.VersionRequestedError))
return new HelpText(HeadingInfo.Default){MaximumDisplayWidth = maxDisplayWidth }.AddPreOptionsLine(Environment.NewLine);
if (!errors.Any(e => e.Tag == ErrorType.HelpVerbRequestedError))
return AutoBuild(parserResult, current => DefaultParsingErrorsHandler(parserResult, current), e => e, maxDisplayWidth: maxDisplayWidth);
var err = errors.OfType<HelpVerbRequestedError>().Single();
var pr = new NotParsed<object>(TypeInfo.Create(err.Type), Enumerable.Empty<Error>());
return err.Matched
? AutoBuild(pr, current => DefaultParsingErrorsHandler(pr, current), e => e, maxDisplayWidth: maxDisplayWidth)
: AutoBuild(parserResult, current => DefaultParsingErrorsHandler(parserResult, current), e => e, true, maxDisplayWidth);
}
It seems to me that it itself calls DefaultParsingErrorsHandler. Yet in my code it looks like we do it again. Is this a mistake in my code or something?
Try removing the call to DefaultParsingErrorsHandler:
var helpText = HelpText.AutoBuild(parserResult, h => h, e =>
{
return e;
});
Console.WriteLine(helpText);
Thanks for the suggestion. I have just compiled using that code and tried --help and it is still doubled.
Is that so? I tested it on the latest version just before posting the suggestion, so it's odd that the solution doesn't work for you. Below is what I tried.
void Main()
{
var parserResult = CommandLine.Parser.Default.ParseArguments<Options>("--help".Split());
parserResult.WithParsed<Options>(options => options.Dump());
parserResult.WithNotParsed<Options>(errs =>
{
var helpText = HelpText.AutoBuild(parserResult, h => h, e =>
{
return e;
});
Console.WriteLine(helpText);
});
}
class Options
{
[Option('n', "name")]
public string Name { get; set; }
}
Then I am doing something wrong at my end. Please see this:
https://www.dropbox.com/s/hzm9k9x570tapa4/CommandLine.mp4?dl=0
These are my command options:
// Define a class to receive parsed values
public class Options
{
[Option('l', "list", Required = false, HelpText = "Builds a list of available calendars. Eg: -l \".\\CalendarList.xml\"")]
public string CalendarListPath { get; set; }
[Option('m', "mode", Required = false, HelpText = "Type of events to add to the calendar. Modes: mwb. Eg: -m mwb")]
public string CalendarEventsMode { get; set; }
[Option('p', "path", Required = false, HelpText = "Path to the events data file. Eg: -p \".\\EventsToAdd.xml\"")]
public string CalendarEventsPath { get; set; }
[Option('t', "tokencache", Required = true, HelpText = "The location of the token cache data file. Eg: -t \"file.dat\"")]
public string TokenCachePath { get; set; }
[Option('e', "errorlog", Required = true, HelpText = "The location for the error log. Eg: -e \"<path to error folder>\"")]
public string ErrorLogFolder { get; set; }
[Option('s', "signout", Required = false, HelpText = "Sign out from Outlook calendar")]
public bool SignOut { get; set; }
//[ParserState]
//public IParserState LastParserState { get; set; }
}
It looks like you are doing everything right. If you run dir in that release folder, does the last modified date of the exe you're running match the last time you rebuilt the project?
Yes
Even if I clean the project and try a normal switch, like -l:
D:\My Programs\2017\OutlookCalIFConsole\OutlookCalIFConsole\bin\Release>outlookcalifconsole --l
OutlookCalIFConsole 2018.4.4.1
Copyright c 2017 - 2018
ERROR(S):
Option 'l, list' has no value.
Required option 't, tokencache' is missing.
Required option 'e, errorlog' is missing.
-l, --list Builds a list of available calendars. Eg: -l ".\CalendarList.xml"
-m, --mode Type of events to add to the calendar. Modes: mwb. Eg: -m mwb
-p, --path Path to the events data file. Eg: -p ".\EventsToAdd.xml"
-t, --tokencache Required. The location of the token cache data file. Eg: -t "file.dat"
-e, --errorlog Required. The location for the error log. Eg: -e "<path to error folder>"
-s, --signout Sign out from Outlook calendar
--help Display this help screen.
--version Display version information.
OutlookCalIFConsole 2018.4.4.1
Copyright c 2017 - 2018
-l, --list Builds a list of available calendars. Eg: -l
".\CalendarList.xml"
-m, --mode Type of events to add to the calendar. Modes: mwb. Eg: -m
mwb
-p, --path Path to the events data file. Eg: -p ".\EventsToAdd.xml"
-t, --tokencache Required. The location of the token cache data file. Eg:
-t "file.dat"
-e, --errorlog Required. The location for the error log. Eg: -e "<path
to error folder>"
-s, --signout Sign out from Outlook calendar
--help Display this help screen.
--version Display version information.
Notice that they are rendered differently. The second is wrapped at shorter width and excludes the error descriptions.
If I revert back to the older code then we get the "erros" listed twice, once in each. So for me, removing the call simply removes the errors from showing a second time but not the parsed options.
OK, I sorted it. I changed to your code and I had to remove this line:
Console.WriteLine(helpText);
I did not realise that the helpText was sent to the console already for me. Doh!
Although, I still ahev my related question about maybe supporting -h and -? for help as standard. And, my other question about why using --version doesn't just show the version info.
Although it works by removing the console writeline code, I am trying to find in your source where it must automatically send the info the the console window. Can’t find it.
And I don’t understand why you didn’t get duplicates since you did use that console line.
Has there been any changes to affect this? Is code newer than the nuget package?
If I'm reading the source correctly (in Parser.cs), the console output happens when you call ParseArguments<T>. Ignoring the use Maybe wrappers, the call stack is:
ParseArguments<T> -> MakeParserResult<T> -> DisplayHelp<T> -> writer.Write
Where writer is ultimately bound to the Parser.HelpWriter property. Unfortunately, parsing and output are tightly coupled in CommandLineParser, so if you need to customize its output beyond the provided facilities (ex. setting the copyright string), you pretty much just have to squelch its output entirely and substitute your own. You can do that by setting ParserSettings.HelpWriter to null and pass that in when you construct your Parser<T>. Or set it to your own TextWriter implementation and try to "mutate" the output as it passes through, but that sounds like an awful, brittle mess.
Nulling the HelpWriter works:
var parserResult = new Parser(with => with.HelpWriter = null).ParseArguments<Options>(args);
Instead of:
var parserResult = CommandLine.Parser.Default.ParseArguments<Options>(args);
Nulling the HelpWriter still shows a default help in my case :
public class Options
{
[Value(0, MetaName = "instructions", HelpText = "Path to instructions file.")]
public string InstructionsFilePath { get; set; }
}
var parser = new Parser(with => with.HelpWriter = null);
var parserResult = parser.ParseArguments<Options>(args);
Parser.Default.ParseArguments<Options>(args)
.WithParsed(_start);
Calling myProgram.exe --help in a CLI returns :
MyProgram 1.0.0.0
Copyright © 2021
--help Display this help screen.
--version Display version information.
instructions (pos. 0) Path to instructions file.
While my understanding is that it should print nothing. Tested on 2.8.0