CLI11 icon indicating copy to clipboard operation
CLI11 copied to clipboard

[Question] How to allow flags in a multiple arguments option (or how to name the remaining() in the help)

Open jmarrec opened this issue 3 years ago • 3 comments

I'm trying to have a subcommand that takes a file, and some extra args, and those extra args are passed down to that file for later processing. (I embed ruby & python, so I want a way to execute that ruby/python file with arguments).

What I'm looking after:

Usage: Products/os-cli11 execute_ruby_script [OPTIONS] path [arguments...]

Positionals:
  path TEXT:FILE REQUIRED     Path to ruby file
  arguments args              Arguments to pass to the ruby file

The issue is that if I pass execute_ruby_script myfile.rb arg1 arg2 it works fine, but execute_ruby_script myfile.rb -x arg2 doesn't: The following argument was not expected: -x

How can I both:

  • Allow passing extra args that might be flags, AND
  • Explicitly say so in the help message?

Thanks a lot for the great lib!

Original code

Original code, also available here: https://godbolt.org/z/vasGMPrrs

Click to Expand Original code
#include <CLI/CLI.hpp>
#include <fmt/format.h>
#include <fmt/std.h>     // Formatting std::filesystem::path
#include <fmt/ranges.h>  // Formatting std::vector

#include <filesystem>
namespace fs = std::filesystem;


int main(int argc, char* argv[]) {
  CLI::App app{"My CLI"};
  app.require_subcommand(1);

  auto* execute_ruby_scriptCommand = app.add_subcommand("execute_ruby_script", "Executes a ruby file");
  fs::path rubyScriptPath;
  execute_ruby_scriptCommand->add_option("path", rubyScriptPath, "Path to ruby file")->required(true)->check(CLI::ExistingFile);
  std::vector<std::string> executeRubyScriptCommandArgs;
  execute_ruby_scriptCommand->add_option("arguments", executeRubyScriptCommandArgs, "Arguments to pass to the ruby file")
     ->required(false)
     ->option_text("args");
  execute_ruby_scriptCommand->callback([&] {
    fmt::print("rubyScriptPath={}\n", rubyScriptPath);
    fmt::print("executeRubyScriptCommandArgs=[{}]\n", executeRubyScriptCommandArgs);
    for (const auto& arg : execute_ruby_scriptCommand->remaining()) {
      fmt::print("Remaining arg: {}\n", arg);
    }
  });

  CLI11_PARSE(app, argc, argv);

}
$ oscli11 execute_ruby_script myfile.rb -x arg2
The following argument was not expected: -x
Run with --help for more information.

Remaining args: it works to forward, but the help doesn't mention it

https://godbolt.org/z/c13db5WvY

It works:

$ oscli11 execute_ruby_script myfile.rb -x arg2
rubyScriptPath="myfile.rb"
remaining=["-x", "arg2"]

But the help doesn't mention it:

$ oscli11 execute_ruby_script --help
Executes a ruby file
Usage: build/oscli11 execute_ruby_script [OPTIONS] path

Positionals:
  path TEXT REQUIRED          Path to ruby file

Options:
  -h,--help                   Print this help message and exit
Click to expand remaining args version
int main(int argc, char* argv[]) {
  CLI::App app{"My CLI"};
  app.require_subcommand(1);

  auto* execute_ruby_scriptCommand = app.add_subcommand("execute_ruby_script", "Executes a ruby file");
  fs::path rubyScriptPath;
  execute_ruby_scriptCommand->add_option("path", rubyScriptPath, "Path to ruby file")->required(true); // ->check(CLI::ExistingFile);
  execute_ruby_scriptCommand->allow_extras(true);
  execute_ruby_scriptCommand->callback([&] {
    fmt::print("rubyScriptPath={}\n", rubyScriptPath);
    fmt::print("remaining=[{}]\n", execute_ruby_scriptCommand->remaining());
  });

  CLI11_PARSE(app, argc, argv);
}

jmarrec avatar Oct 28 '22 09:10 jmarrec

I just realized that with the original version I can add a double dash in between the file and the args and it works.

$ execute_ruby_script myfile.rb -- -x arg2 -o arg4
rubyScriptPath="myfile.rb"
executeRubyScriptCommandArgs=["-x", "arg2", "-o", "arg4"]
remaining=["--"]

Is there to way to add that double dash into the help message (or even better, REQUIRE it)? That would solve my problem.

jmarrec avatar Oct 28 '22 09:10 jmarrec

You can use the footer to add a user specified note.
after #786 gets merged there will be a usage field that can also be user specified.

A couple other notes. One option might be to use positionals_at_end() then assuming the *.rb get placed in a positional argument. Everything after that would be considered a positional argument and could be placed in a positional vector.

phlptp avatar Oct 28 '22 20:10 phlptp

Thanks for the tips @phlptp.

Would a feature of adding a mandatory separator --, for eg between two positionals, be of interest to the CLI11 project? app.add_separator() and it would include it in the help message + check for it?

jmarrec avatar Oct 31 '22 15:10 jmarrec