Search code examples
c#.net-corecommand-line-interfacecommand-line-argumentssystem.commandline

Why is C# app not aborted despite the required global option missing?


I have difficulties understanding how the System.CommandLine works and have prepared a short .Net fiddle to demonstrate my issue.

I am trying to create a C# console app, which would be called with --nodes option and be passed one or many numerical ids:

public static async Task Main(string[] args)
{
    Option<long[]> nodesOption = new("-n", "--nodes")
    {
        Arity = ArgumentArity.OneOrMore,
        Description = "Numerical node ids",
        IsRequired = true,
    };

    RootCommand rootCommand = new("A test app for saving node ids");
    rootCommand.AddGlobalOption(nodesOption);
    await rootCommand.InvokeAsync(args);

    Console.WriteLine("Why is this line printed?");
}

When I call the above code however without specifying any options, it does print the warning Option '-n' is required., but the execution is not aborted.

Shouldn't the app exit with code 1 when an isRequired global option is missing?

If it shouldn't, how can I change the app behaviour to exit with code 1?

And also I wonder, how to specify a new[] { 1234567890 } as the default value for the option, I cannot find it in the System.CommandLine doc.

Below is a screenshot of my test case and its output:

screenshot with C# code


Solution

  • Yes, the execution of a command is not supposed to terminate the whole application, that is by design of the command-linetoolset.

    Now, for the detecting. The rootCommand.InvokeAsync method would return non-zero result if not completed successfully. So, in your case simply check for the output:

    int result = await rootCommand.InvokeAsync(args);
    
    if (result != 0)
    {
        // Handle the failure of the rootCommand's call.
        return;
    }
    

    As for the default value, there is actually an overload which allows you to specify a delegate which in turn should provide default values if none are input by the user:

    Option<long[]> nodesOption = new(
        new[] { "-n", "--nodes" }, // Your aliases
        () => new[] { 1234567890L } // The delegate with the default values.
    )