I'm using clap and I get a unexpected behaviour when trying to parse arguments.
My command line tool is supposed to work like this
foo -u <user> <command>
e.g.:
foo -u jack echo s
foo -u paul ls -al
I need to get options such as user, but the <command>
itself, I need to be the rest of the args.
The code below results in a behavior where I can't get the value of <command>
unless it is quoted:
foo -u jack echo s
error: Found argument 's' which wasn't expected, or isn't valid in this context
Whereas this works fine:
foo -u jack 'echo s'
Is there any way of avoiding the quotes?
let matches = App::new("foo")
.version("0.1")
.arg(
Arg::with_name("user")
.short("u")
.long("user")
.required(true)
.takes_value(true),
)
.arg(
Arg::with_name("command")
.help("The command to run")
.required(true)
.takes_value(true),
)
.get_matches();
I've also opened an issue on the clap repository.
By default, clap will only parse any argument once. This means that in -u jack echo s
, it will parse -u jack
as the "user" option, echo
as the "command" argument, and have an argument s
that it doesn't know what to do with (hence it "wasn't expected").
To retrieve all trailing arguments you need to set .multiple(true)
on the last argument (in your case "command") so it parses all the remaining arguments.
Additionally set the following options on the clap command to avoid parsing remaining arguments as clap arguments:
.setting(AppSettings::TrailingVarArg)
: to indicate that clap should stop parsing for other flags/options after the last positional argument..setting(AppSettings::AllowLeadingHyphen)
: to allow argument values with leading hyphens like -al
.Here's an example:
let matches = App::new("foo")
.version("0.1")
.setting(clap::AppSettings::TrailingVarArg)
.setting(clap::AppSettings::AllowLeadingHyphen)
.arg(
Arg::with_name("user")
.short("u")
.long("user")
.required(true)
.takes_value(true),
)
.arg(
Arg::with_name("command")
.help("The command to run")
.required(true)
.takes_value(true)
.multiple(true),
)
// parse as if program ran as: foo -u paul ls -al
.get_matches_from(&["foo", "-u", "paul", "ls", "-al"]);
let command: Vec<&str> = matches.values_of("command").unwrap().collect();
println!("{:?}", command); // ["ls", "-al"]