command-line-api
command-line-api copied to clipboard
Invoke a parent command without specifying its subcommands
In my use case, both the following invocations are expected.
$ myprog --load checkpoint.json
$ myprog process --file test.json
where the root command myprog
with the subcommand process
can be called without specifying a subcommand. Is this use-case currently supported in System.CommandLine
?
Using a code similar to the following, I get Required command was not provided
error when making the first invocation above.
using System.CommandLine;
var loadOpt = new Option<FileInfo?>("--load");
var rootCmd = new RootCommand() { loadOpt };
var fileOpt = new Option<FileInfo?>("--file");
var subCmd = new Command(name: "process") { fileOpt };
rootCmd.AddCommand(subCmd);
The Required command was not provided
error is generated by ParseResultVisitor.ValidateCommandHandler. It looks like you could just give the parent command a non-null handler and then it would be OK.
Thanks! To confirm, adding the following fixes the issue (though I guess it would have been better if it was not required :)).
rootCmd.SetHandler(x => { });
I wonder if it would be possible to specify options that can be used without subcommands.
For instance, with the following:
var workingDirOption = new Option<DirectoryInfo?>("--working-dir");
rootCmd.AddGlobalOption(workingDirOption);
Allowed:
$myprog --load checkpoint.json
Disallowed:
myprog --working-dir .
I don't understand what you request. The following program built with System.CommandLine 2.0.0-beta4.22272.1
using System.CommandLine;
var loadOpt = new Option<FileInfo?>("--load");
var rootCmd = new RootCommand() { loadOpt };
var fileOpt = new Option<FileInfo?>("--file");
var subCmd = new Command(name: "process") { fileOpt };
rootCmd.AddCommand(subCmd);
var workingDirOption = new Option<DirectoryInfo?>("--working-dir");
rootCmd.AddGlobalOption(workingDirOption);
rootCmd.SetHandler(x => { });
return rootCmd.Invoke(args);
gives the following results when executed with different command lines:
-
--load checkpoint.json
: OK -
process --load checkpoint.json
: Unrecognized command or argument -
--working-dir .
: OK -
process --working-dir .
: OK -
--file stuff
: Unrecognized command or argument -
process --file stuff
: OK -
--not-defined at-all
: Unrecognized command or argument -
process --not-defined at-all
: Unrecognized command or argument
This seems to cover all the combinations already. Or is your request about other subcommands?
I want the following to fail.
--working-dir .: OK
Without rootCmd.SetHandler(x => { });
. In other words, I am curious about how to change the code for the following use case.
-
--load checkpoint.json
: OK -
--working-dir .
: FAIL
So you want --working-dir
to behave the same way as --file
?
Yes, though I am adding the workingDirOpt
to the rootCmd
since I want every subcommand to have that option. So, I am trying to avoid the following.
var subCmd1 = new Command(name: "process") { workingDirOpt, fileOpt };
var subCmd2 = new Command(name: "delete") { workingDirOpt };
var subCmd3 = new Command(name: "post") { workingDirOpt, anotherOpt, anotherOpt2 };
So, I am interested in the following invocations:
-
OK:
$ myProg --load checkpoint.json $ myProg process --working-dir . --file x.txt $ myProg delete --working-dir .
-
FAIL:
$ myProg $ myProg --working-dir . $ myProg process --load checkpoint.json $ myProg process --load checkpoint.json --working-dir . --file x.txt
How about deriving a class from Command and adding the option in the constructor?
A validator on the root command (or on the option?) could also be a way to reject the option if no subcommand is provided, but then the option would still appear in help and completions… those may be customizable but it would get yet more complex.
That seems doable, though a bit overkill IMHO, and comes with issues in the help display as you suggested.