Reputation: 880
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.
Upvotes: 7
Views: 3935
Reputation: 23228
As of the current clap
version (3.2.12), the above code is no longer valid since the API has gone through some changes plus deprecations. For e.g.
short
now accepts a char
instead of a &str
with_name
is now
deprecated and replaced with new
setting
is now deprecated in
favor of specific function names for each configurationmultiple
now replaced by multiple_values
values_of
deprecated in favor of get_many
The updated snippet is:
#[test]
fn test_parse_config() {
let matches = App::new("foo")
.version("0.1")
.allow_hyphen_values(true)
.trailing_var_arg(true)
.arg(
Arg::new("user")
.short('u')
.long("user")
.required(true)
.takes_value(true),
)
.arg(
Arg::new("command")
.help("The command to run")
.required(true)
.takes_value(true)
.multiple_values(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.get_many::<String>("command").unwrap().
map(|s| s.as_str()).collect();
println!("{:?}", command); // ["ls", "-al"]
}
Upvotes: 2
Reputation: 40894
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"]
Upvotes: 10