Command-line argument parsing
Recipe | Crates | Categories |
---|---|---|
Using clap's builder API | ||
Using clap 's derive API | ||
lexopt | ||
Parse command-line arguments with pico-args |
| structopt
| |
|
Using clap's builder API
This application describes the structure of its command-line interface using clap
⮳'s builder style. The documentation⮳ gives two other possible ways to instantiate an application.
In the builder style, with_name
is the unique identifier that value_of
will use to retrieve the value passed. The clap::Arg::short
⮳ and clap::Arg::long
⮳ options control the flag the user will be expected to type; short flags look like -f
and long flags look like --file
.
use std::path::PathBuf; use clap::Arg; use clap::Command; use clap::value_parser; fn cli() -> Command { clap::Command::new("My Test Program") .bin_name("test_app") .version("0.1.0") .author("Hackerman Jones <hckrmnjones@hack.gov>") .about("Teaches argument parsing") // First possible argument: --num or -n .arg(Arg::new("num") .short('n') // -n argument .long("number") // --number long-form argument .value_name("NUMBER") // placeholder for the argument's value in the help message / usage. .required(false) .help("Enter your favorite number")) // Second possible argument: --file or -f .arg(Arg::new("file") .short('f') .long("file") .value_parser(value_parser!(PathBuf)) .help("Enter the path of a file")) // You can also use the arg! macro: .arg(clap::arg!(-c --config <CONFIG> // "Optionally sets a config file to use")) } fn main() { let matches = cli().get_matches_from(["test_app", "-n", "42", "--file", "README.md"]); // In a real program, use the following to retrieve arguments from the // command line: let matches = cli().get_matches(); if let Some(num) = matches.get_one::<String>("num") { println!("Value for num: {num}"); } if let Some(file_path) = matches.get_one::<PathBuf>("file") { println!("Value for file: {}", file_path.display()); } }
Usage information is generated by clap
⮳. The usage for the example application looks like this.
My Test Program 0.1.0
Hackerman Jones <hckrmnjones@hack.gov>
Teaches argument parsing
USAGE:
testing [OPTIONS]
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
-f, --file <file> A cool file
-n, --number <num> Five less than your favorite number
We can test the application by running a command like the following.
cargo run -- -f myfile.txt -n 251
The output is:
The file passed is: myfile.txt
Your favorite number must be 256.
Using clap
's derive API
clap_derive
⮳ simplifies CLI creation in Rust via a derive
⮳ macro, automatically generating command-line argument parsing logic from struct
definitions.
use std::path::PathBuf; use anyhow::Result; use clap::Parser; use clap::Subcommand; // The struct declaring the desired command-line arguments and // commands // The `derive` feature flag is required (see Cargo.toml). #[derive(Parser, Debug)] // Reads the following attributes the from the package's `Cargo.toml` // Alternatively, use #[command(name = "MyApp")] ... #[command(author, version, about, long_about = None)] // Displays Help if no arguments are provided #[command(arg_required_else_help = true)] pub struct Cli { // Positional argument example /// The pattern to look for (the doc comment appears in the help) pattern: Option<String>, /// Required argument example (with default value and validation) #[arg(default_value_t = 8080)] #[arg(value_parser = clap::value_parser!(u16).range(1..))] port: u16, // Named argument example: the path to the file to look into #[arg(short, long)] path: Option<PathBuf>, /// Count example: turn debugging information on #[arg(short, long, action = clap::ArgAction::Count)] debug: u8, // // Alternatively, use the // // `clap-verbosity-flag` crate: // // It adds the following flags through the entire program: // // -q silences output // // -v show warnings // // -vv show info // // -vvv show debug // // -vvvv show trace // // By default, this will only report errors. // #[clap(flatten)] // verbose: clap_verbosity_flag::Verbosity, // Subcommands #[command(subcommand)] pub command: Option<Commands>, } // The subcommands #[derive(Subcommand, Debug)] pub enum Commands { /// Read something #[command(arg_required_else_help = true)] Read { /// A boolean flag #[arg(short, long)] all: bool, #[arg(required = true)] file: Vec<PathBuf>, }, /// Say something Tell, } fn main() -> Result<()> { // `Clap` returns a Cli struct populated from `std::env::args_os()`... let cli = Cli::try_parse()?; // You also could use `parse()` // The argument values we got... println!("Path: {:?}", cli.path); // Use `unwrap_or` to set defaults for optional arguments // (or use `default_value_t` as above). println!("Pattern: {:?}", cli.pattern.unwrap_or("".to_string())); // You can see how many times a particular flag or argument occurred // Note, only flags can have multiple occurrences match cli.debug { 0 => println!("Debug mode is off"), 1 => println!("Debug mode is kind of on"), 2 => println!("Debug mode is on"), _ => println!("Don't be crazy"), } // // Alternatively, use the `verbose` flag, if configured above // env_logger::Builder::new() // .filter_level(cli.verbose.log_level_filter()) // .init(); // Check for the existence of subcommands match &cli.command { Some(Commands::Read { all, file }) => { if *all { println!("Read all..."); } else { println!("Read just one..."); } println!("{:?}", file); } Some(Commands::Tell) => { println!("{}", 42); } None => {} } Ok(()) }
See also
lexopt
lexopt
⮳ offers efficient and ergonomic command-line argument parsing. It prioritizes simplicity and performance with a declarative approach, avoiding complex macros. Expect fast compile times, fast runtime, but a tool pedantic about correctness. Its API is less ergonomic.
// Simple command line argument parser struct Args { name: String, age: u32, } fn get_args() -> anyhow::Result<Args> { // Allows writing Short/Long/Value without an Arg prefix // and adds convenience methods to OsString: use lexopt::prelude::*; let mut parser = lexopt::Parser::from_iter(&["myapp", "-a", "30", "John"]); // In a real world app, create a parser using `std::env::args_os` // let mut parser = lexopt::Parser::from_env(); let mut name = String::new(); let mut age = 0; // Get the name of the command, as in the zeroth argument of the process let bin_name: String = parser.bin_name().unwrap_or("unknown").into(); // Loop the next option or positional argument while let Some(arg) = parser.next()? { match arg { // `Short` and `Long` indicate an option, here -a or --age Short('a') | Long("age") => { // `value()` returns the value that belongs to the option as a // standard `OsString`. age = parser.value()?.parse()?; } // Positional argument Value(val) => { // `string()` converts the `OsString` into a `String` if it is // valid Unicode. name = val.string()?; } Long("help") => { println!("Usage: {bin_name} [-a|--age=NUM] [NAME]"); std::process::exit(0); } _ => { return Err(anyhow::anyhow!("Unexpected flag or option")); } } } Ok(Args { name, age }) } fn main() -> anyhow::Result<()> { let args = get_args()?; println!("Name: {}", args.name); println!("Age: {}", args.age); Ok(()) } #[test] fn test() -> anyhow::Result<()> { main()?; Ok(()) }
Parse command-line arguments with pico-args
The pico-args
⮳ crate is a small and fast library for parsing command-line arguments in Rust.
pico-args
⮳ is tiny, dependency-free, and designed for extremely fast and minimal command-line argument parsing. It focuses on speed and small binary size, making it suitable for resource-constrained environments or applications where performance is critical. It offers a simple API for basic argument parsing but may not be as feature-rich as larger crates. Note the following:
- No help generation.
- Only flags, options, free arguments and subcommands are supported.
- No combined flags (like -vvv or -abc).
- Options can be separated by a space, = (with the
eq-separator
feature) or nothing. - Arguments can be in any order.
- Non UTF-8 arguments are supported.
- Ergonomic API.
use pico_args::Arguments; fn main() -> anyhow::Result<()> { // Returns the arguments that this program was started with. // The executable path will be removed. let mut args = Arguments::from_env(); // Parse named arguments // --name and --age are optional key-value pairs, parsed using the // `FromStr`` trait. This is a shorthand for // `opt_value_from_fn("--name", FromStr::from_str)` let name: Option<String> = args.opt_value_from_str("--name")?; let age: Option<u32> = args.opt_value_from_str("--age")?; // Parse a flag let verbose = args.contains("--verbose"); // Parse a positional ("free-standing") argument let file: Option<String> = args.opt_free_from_str()?; // Print the parsed arguments if let Some(name) = name { println!("Name: {}", name); } if let Some(age) = age { println!("Age: {}", age); } if verbose { println!("Verbose mode is on"); } if let Some(file) = file { println!("File: {}", file); } // Check for unused arguments let remaining_args = args.finish(); if !remaining_args.is_empty() { eprintln!("Warning: unused arguments! {:?}", remaining_args); } Ok(()) }