Subcommands and shared options
Build clap subcommands with shared options, argument groups, conflicts, and environment defaults. Handle paths and trailing arguments.
Subcommands#
Use subcommands when your tool performs distinct actions such as add, list, and remove.
use clap::{Parser, Subcommand};
/// Keep a small list of tasks.
#[derive(Parser)]
#[command(version, propagate_version = true)]
struct Cli {
#[command(subcommand)]
command: Command,
}
#[derive(Subcommand)]
enum Command {
/// Add one task.
Add {
title: String,
#[arg(short, long)]
priority: Option<u8>,
},
/// Display tasks.
List {
#[arg(long)]
all: bool,
},
}
fn main() {
match Cli::parse().command {
Command::Add { title, priority } => println!("add {title} {priority:?}"),
Command::List { all } => println!("list all={all}"),
}
}cargo run -- add "Pay rent" -p 2
# add Pay rent Some(2)
cargo run -- list --all
# list all=true
cargo run -- add --helpA field of type Command requires a subcommand and shows help when one is missing. Option<Command> allows no subcommand. The help subcommand is generated by clap.
Conflicts, dependencies, and groups#
Describe invalid combinations in the parser so users see help before work begins.
use clap::{Args, Parser};
#[derive(Parser)]
struct Cli {
#[command(flatten)]
source: Source,
/// Endpoint that needs --token.
#[arg(long, requires = "token")]
remote: Option<String>,
#[arg(long)]
token: Option<String>,
}
#[derive(Args)]
#[group(required = true, multiple = false)]
struct Source {
/// Read from a file.
#[arg(long)]
file: Option<String>,
/// Read from standard input.
#[arg(long)]
stdin: bool,
}
fn main() {
let _ = Cli::parse();
}cargo run -- --file data.txt
cargo run -- --stdin --remote https://example.test --token secret
cargo run -- --file data.txt --stdin # conflicting source options
cargo run -- --stdin --remote https://example.test # --token requiredA group with required = true, multiple = false asks for exactly one member. The ID in requires = "token" is a Rust field ID, not the spelling of its long option. For a single pair, #[arg(conflicts_with = "stdin")] is often enough.
Pass real credentials through environment variables or secure input. Literal command-line tokens can remain in shell history.
Environment variables and precedence#
Let users set stable defaults without typing the same option every time.
Enable clap’s env feature. An explicit command line value wins over an environment value, which wins over a declared default. Clap does not read .env files by itself.
use clap::Parser;
#[derive(Parser, Debug)]
struct Cli {
#[arg(long, env = "SHELF_ENDPOINT", default_value = "http://localhost:3000")]
endpoint: String,
#[arg(long, env = "SHELF_TOKEN", hide_env_values = true)]
token: Option<String>,
}
fn main() {
let cli = Cli::parse();
println!("endpoint={}", cli.endpoint);
println!("token set={}", cli.token.is_some());
}SHELF_ENDPOINT=https://example.test cargo run --
# endpoint=https://example.test
SHELF_ENDPOINT=https://example.test cargo run -- --endpoint http://localhost:9000
# endpoint=http://localhost:9000hide_env_values avoids showing the current environment value in help output. Avoid printing credential values yourself, including debug-printing the entire parsed struct.
Paths, Unicode, and the -- separator#
Accept file paths without assuming every OS path is valid UTF-8.
use std::path::PathBuf;
use clap::Parser;
#[derive(Parser)]
struct Cli {
/// File to read.
input: PathBuf,
/// Write results here.
#[arg(short, long)]
output: Option<PathBuf>,
}
fn main() {
let cli = Cli::parse();
println!("{:?} -> {:?}", cli.input, cli.output);
}cargo run -- -- -notes.txt
# The first -- ends Cargo's options; the second ends shelf's options.
cargo run -- -o result.txt -- -notes.txtUse OsString for raw OS strings and PathBuf for file paths. With a String, non-UTF-8 input cannot be represented. For a negative number intended as a positional argument, -- -5 avoids confusion with an option.
For an option whose value may begin with -, use --option=-value or configure allow_hyphen_values only when that behavior suits the option.
Help improve CodeRef
Allow Google Analytics to measure page visits and use of the guides? It uses cookies after you accept. Your choice won’t affect access to the site. Privacy notice