Ler Argumentos da Linha de Comando em Rust
Processar argumentos de linha de comando é fundamental para criar ferramentas CLI. Rust oferece std::env::args na biblioteca padrão e a crate clap para parsing avançado com validação automática.
Método básico: std::env::args
A forma mais simples, sem dependências externas:
use std::env;
fn main() {
// args() retorna um iterador sobre os argumentos
let args: Vec<String> = env::args().collect();
println!("Total de argumentos: {}", args.len());
println!("Programa: {}", args[0]);
// Acessar argumentos por posição
if args.len() > 1 {
println!("Primeiro argumento: {}", args[1]);
}
// Iterar sobre todos os argumentos (pulando o nome do programa)
println!("\nArgumentos:");
for (i, arg) in args.iter().skip(1).enumerate() {
println!(" [{}] = {}", i, arg);
}
// Exemplo prático: calculadora simples
if args.len() == 4 {
let a: f64 = args[1].parse().unwrap_or(0.0);
let op = &args[2];
let b: f64 = args[3].parse().unwrap_or(0.0);
let resultado = match op.as_str() {
"+" => a + b,
"-" => a - b,
"*" | "x" => a * b,
"/" => if b != 0.0 { a / b } else { f64::NAN },
_ => {
eprintln!("Operador desconhecido: {}", op);
return;
}
};
println!("{} {} {} = {}", a, op, b, resultado);
} else if args.len() > 1 {
eprintln!("Uso: {} <número> <operador> <número>", args[0]);
}
}
Execução e saída:
$ cargo run -- 10 + 32
Total de argumentos: 4
Programa: target/debug/meu_app
Primeiro argumento: 10
Argumentos:
[0] = 10
[1] = +
[2] = 32
10 + 32 = 42
Parsing manual de flags
Processe flags e opções manualmente:
use std::env;
use std::process;
struct Config {
arquivo: String,
linhas: usize,
verbose: bool,
formato: String,
}
fn parse_args() -> Config {
let args: Vec<String> = env::args().collect();
let mut config = Config {
arquivo: String::new(),
linhas: 10,
verbose: false,
formato: "texto".to_string(),
};
let mut i = 1;
while i < args.len() {
match args[i].as_str() {
"-n" | "--linhas" => {
i += 1;
config.linhas = args.get(i)
.and_then(|v| v.parse().ok())
.unwrap_or_else(|| {
eprintln!("Erro: -n requer um número");
process::exit(1);
});
}
"-v" | "--verbose" => config.verbose = true,
"-f" | "--formato" => {
i += 1;
config.formato = args.get(i).cloned().unwrap_or_else(|| {
eprintln!("Erro: -f requer um valor");
process::exit(1);
});
}
"-h" | "--help" => {
println!("Uso: programa [OPÇÕES] <arquivo>");
println!(" -n, --linhas NUM Número de linhas (padrão: 10)");
println!(" -v, --verbose Modo verboso");
println!(" -f, --formato FMT Formato de saída (texto/json)");
println!(" -h, --help Mostrar ajuda");
process::exit(0);
}
arg if arg.starts_with('-') => {
eprintln!("Opção desconhecida: {}", arg);
process::exit(1);
}
_ => config.arquivo = args[i].clone(),
}
i += 1;
}
if config.arquivo.is_empty() {
eprintln!("Erro: arquivo é obrigatório. Use --help para ajuda.");
process::exit(1);
}
config
}
fn main() {
let config = parse_args();
println!("Arquivo: {}", config.arquivo);
println!("Linhas: {}", config.linhas);
println!("Verbose: {}", config.verbose);
println!("Formato: {}", config.formato);
}
Execução:
$ cargo run -- -v -n 20 -f json dados.txt
Arquivo: dados.txt
Linhas: 20
Verbose: true
Formato: json
Clap com derive macros (recomendado)
A crate clap gera automaticamente parsing, validação e mensagens de ajuda:
Cargo.toml:
[dependencies]
clap = { version = "4", features = ["derive"] }
use clap::Parser;
/// Ferramenta de processamento de arquivos de texto
#[derive(Parser, Debug)]
#[command(name = "textproc", version, about)]
struct Args {
/// Arquivo de entrada para processar
arquivo: String,
/// Número máximo de linhas para ler
#[arg(short = 'n', long, default_value_t = 10)]
linhas: usize,
/// Padrão de busca (regex)
#[arg(short, long)]
busca: Option<String>,
/// Formato de saída
#[arg(short, long, default_value = "texto", value_parser = ["texto", "json", "csv"])]
formato: String,
/// Ativar modo verboso
#[arg(short, long)]
verbose: bool,
/// Diretório de saída
#[arg(short = 'o', long, default_value = ".")]
output: String,
}
fn main() {
let args = Args::parse();
if args.verbose {
println!("Modo verboso ativado");
println!("Configuração: {:#?}", args);
}
println!("Processando: {}", args.arquivo);
println!("Linhas: {}", args.linhas);
println!("Formato: {}", args.formato);
println!("Output: {}", args.output);
if let Some(busca) = &args.busca {
println!("Buscando: '{}'", busca);
}
}
Execução e saída:
$ cargo run -- dados.txt -n 50 --formato json -v --busca "erro"
Modo verboso ativado
Configuração: Args { arquivo: "dados.txt", linhas: 50, busca: Some("erro"), formato: "json", verbose: true, output: "." }
Processando: dados.txt
Linhas: 50
Formato: json
Output: .
Buscando: 'erro'
$ cargo run -- --help
Ferramenta de processamento de arquivos de texto
Usage: textproc [OPTIONS] <ARQUIVO>
Arguments:
<ARQUIVO> Arquivo de entrada para processar
Options:
-n, --linhas <LINHAS> Número máximo de linhas [default: 10]
-b, --busca <BUSCA> Padrão de busca (regex)
-f, --formato <FORMATO> Formato de saída [default: texto] [possible values: texto, json, csv]
-v, --verbose Ativar modo verboso
-o, --output <OUTPUT> Diretório de saída [default: .]
-h, --help Print help
-V, --version Print version
Subcomandos com clap
Crie CLIs com subcomandos (como git commit, git push):
Cargo.toml:
[dependencies]
clap = { version = "4", features = ["derive"] }
use clap::{Parser, Subcommand};
#[derive(Parser)]
#[command(name = "tarefas", about = "Gerenciador de tarefas CLI")]
struct Cli {
#[command(subcommand)]
comando: Comandos,
}
#[derive(Subcommand)]
enum Comandos {
/// Adicionar uma nova tarefa
Add {
/// Descrição da tarefa
descricao: String,
/// Prioridade (1-5)
#[arg(short, long, default_value_t = 3)]
prioridade: u8,
},
/// Listar todas as tarefas
List {
/// Mostrar apenas tarefas com esta prioridade
#[arg(short, long)]
prioridade: Option<u8>,
/// Mostrar tarefas concluídas
#[arg(long)]
concluidas: bool,
},
/// Marcar tarefa como concluída
Done {
/// ID da tarefa
id: u32,
},
/// Remover uma tarefa
Remove {
/// ID da tarefa
id: u32,
/// Não pedir confirmação
#[arg(short, long)]
force: bool,
},
}
fn main() {
let cli = Cli::parse();
match cli.comando {
Comandos::Add { descricao, prioridade } => {
println!("Nova tarefa: '{}' (prioridade: {})", descricao, prioridade);
}
Comandos::List { prioridade, concluidas } => {
println!("Listando tarefas:");
if let Some(p) = prioridade {
println!(" Filtro: prioridade = {}", p);
}
println!(" Mostrar concluídas: {}", concluidas);
}
Comandos::Done { id } => {
println!("Tarefa #{} marcada como concluída!", id);
}
Comandos::Remove { id, force } => {
if force {
println!("Tarefa #{} removida (sem confirmação)", id);
} else {
println!("Tem certeza que deseja remover tarefa #{}? (use --force)", id);
}
}
}
}
Execução:
$ cargo run -- add "Aprender Rust" --prioridade 5
Nova tarefa: 'Aprender Rust' (prioridade: 5)
$ cargo run -- list --prioridade 5
Listando tarefas:
Filtro: prioridade = 5
Mostrar concluídas: false
$ cargo run -- done 42
Tarefa #42 marcada como concluída!
Veja também
- Ler Input do Usuário — entrada interativa do teclado
- Ler Arquivo em Rust — processe arquivos passados como argumento
- Formatar Strings — formate a saída do seu CLI
- Converter String para Número — parse de argumentos numéricos
- Escrever em Arquivo — salve resultados do seu CLI
- Documentação da crate: clap