Kommandozeilenargumente in Rust
Viele kleine Übungsprojekte, die sich lohnen – etwa eine To-do-Liste oder ein Wortzähler –, sollen ihre Eingaben von der Kommandozeile bekommen, statt feste Werte im Quellcode zu haben. Auf dieser Seite lernst du die Grundlagen.
Argumente mit std::env::args lesen
Abschnitt betitelt „Argumente mit std::env::args lesen“use std::env;
fn main() { let args: Vec<String> = env::args().collect(); println!("{:?}", args);}Angenommen, du kompilierst das zu einem Programm namens greet und rufst es so auf:
./greet AliceDann siehst du:
["greet", "Alice"]Das erste Element ist immer der Name des Programms selbst. Die Argumente, die du tatsächlich eingegeben hast, beginnen bei Index 1.
Ein bestimmtes Argument lesen
Abschnitt betitelt „Ein bestimmtes Argument lesen“use std::env;
fn main() { let args: Vec<String> = env::args().collect();
if args.len() < 2 { println!("Verwendung: greet <name>"); return; }
let name = &args[1]; println!("Hallo, {name}!");}Mit cargo run -- Alice bekommst du:
Hallo, Alice!Startest du das Programm ohne Argumente, siehst du statt eines Absturzes den Hinweis zur Verwendung. Weil du args.len() prüfst, bevor du auf einen Index zugreifst, gibt es keinen Panic, wenn jemand vergisst, etwas zu übergeben.
Ein Argument in eine Zahl umwandeln
Abschnitt betitelt „Ein Argument in eine Zahl umwandeln“Jedes Argument kommt als String an. Deshalb brauchst du oft .parse() (von der Seite Typumwandlung), um daraus eine Zahl zu machen:
use std::env;
fn main() { let args: Vec<String> = env::args().collect();
if args.len() < 2 { println!("Verwendung: square <zahl>"); return; }
match args[1].parse::<i32>() { Ok(n) => println!("{}", n * n), Err(_) => println!("Das ist keine gültige Zahl"), }}Ein einfaches Flag von Hand auswerten
Abschnitt betitelt „Ein einfaches Flag von Hand auswerten“Ein Flag ist ein Argument, das etwas einschaltet, zum Beispiel --shout. Bevor du zu einem Crate greifst, lohnt sich ein Blick darauf, wie du so etwas selbst auswertest. Der Trick, der das leicht testbar macht: Steck die Logik in eine Funktion, die die Argumente als Slice &[String] bekommt. Dann kann main die echten Argumente übergeben, und beim Ausprobieren gibst du einfach eine selbst gebaute Liste hinein:
fn run(args: &[String]) { let mut shout = false; let mut names = Vec::new();
// Index 0 überspringen, das ist der Programmname for arg in &args[1..] { if arg == "--shout" { shout = true; } else { names.push(arg); } }
if names.is_empty() { println!("Verwendung: greet [--shout] <name>..."); return; }
for name in names { let line = format!("Hallo, {name}!"); if shout { println!("{}", line.to_uppercase()); } else { println!("{line}"); } }}
fn main() { // In einem echten Programm würdest du schreiben: // let args: Vec<String> = std::env::args().collect(); // Hier bauen wir die Liste von Hand, damit es auch im Playground funktioniert. let args = vec![ String::from("greet"), String::from("--shout"), String::from("Ana"), String::from("Ben"), ]; run(&args);}Ausgabe:
HALLO, ANA!HALLO, BEN!Ein paar Dinge, die dir auffallen sollten:
&args[1..]sind alle Argumente außer dem Programmnamen. Das Flag darf überall stehen:greet Ana --shout Benfunktioniert auch.- Nimm
--shoutaus der Liste und starte das Programm noch einmal: Dann bekommst du die normalen Begrüßungen. Entfernst du auch die Namen, siehst du den Hinweis zur Verwendung. - Weil es
runegal ist, woher seine Liste kommt, musst du inmainnur eine Zeile ändern, umstd::env::args().collect()einzusetzen.
Für ein oder zwei Flags reicht das völlig. Willst du aber auch Kurzformen (-s), Werte nach einem Flag (--count 3) und eine --help-Ausgabe, wächst die handgeschriebene Schleife schnell. Das ist der Moment, um auf clap umzusteigen.
Für mehr als ein paar Argumente: clap
Abschnitt betitelt „Für mehr als ein paar Argumente: clap“Sobald du Flags (--verbose), optionale Werte oder eine hilfreiche Ausgabe für --help brauchst, wird das Hantieren mit Indizes in args schnell unübersichtlich. Die übliche Lösung ist das Crate clap (auf Englisch). Du fügst es so hinzu:
cargo add clap --features deriveuse clap::Parser;
#[derive(Parser)]struct Args { name: String,
#[arg(short, long, default_value_t = 1)] count: u32,}
fn main() { let args = Args::parse(); for _ in 0..args.count { println!("Hallo, {}!", args.name); }}Damit bekommst du das Auswerten der Argumente, ihre Prüfung und eine funktionierende --help-Ausgabe – alles automatisch aus der Struct-Definition erzeugt. Für schnelle Skripte brauchst du clap nicht. Aber für alles, was du öfter als einmal benutzt, spart es dir eine Menge Handarbeit.
Übungen
Abschnitt betitelt „Übungen“Der Playground kann keine echten Argumente übergeben. Deshalb starten beide Übungen mit einem selbst gebauten Vec<String>, der genauso aussieht wie das, was env::args().collect() dir liefern würde. Auf deinem eigenen Rechner ersetzt du ihn durch die echten Argumente und probierst cargo run -- ... aus.
1. Die Argumente auflisten
Abschnitt betitelt „1. Die Argumente auflisten“Du hast vec!["notes", "add", "Milch kaufen"] (als Strings). Gib aus, wie viele Argumente der Benutzer eingegeben hat (ohne den Programmnamen), und danach jedes Argument mit seiner Position, beginnend bei 1.
Tipp: Die Anzahl ist args.len() - 1. Eine Schleife for i in 1..args.len() liefert dir jeden Index nach dem Programmnamen.
Lösung anzeigen
fn main() { // stell dir vor, das kommt von std::env::args().collect() let args = vec![ String::from("notes"), String::from("add"), String::from("Milch kaufen"), ];
println!("Du hast {} Argumente übergeben", args.len() - 1); for i in 1..args.len() { println!("{i}: {}", args[i]); }}Ausgabe:
Du hast 2 Argumente übergeben1: add2: Milch kaufen"Milch kaufen" zählt als ein Argument: Auf einer echten Kommandozeile schreibst du es in Anführungszeichen (cargo run -- add "Milch kaufen"), und die Shell übergibt es als einen einzigen String. Index 0 ist weiterhin der Programmname, deshalb beginnt die Schleife bei 1.
2. Die Zahlen aufsummieren
Abschnitt betitelt „2. Die Zahlen aufsummieren“Du hast vec!["sum", "4", "10", "x", "6"]. Addiere jedes Argument, das eine gültige Zahl ist, und gib für jedes andere eine Warnung aus. Zum Schluss gibst du die Summe aus.
Tipp: Lauf mit einer Schleife über &args[1..] und nimm match arg.parse::<i32>(), wie in Ein Argument in eine Zahl umwandeln. Leg let mut total = 0; außerhalb der Schleife an.
Lösung anzeigen
fn main() { let args = vec![ String::from("sum"), String::from("4"), String::from("10"), String::from("x"), String::from("6"), ];
let mut total = 0; for arg in &args[1..] { match arg.parse::<i32>() { Ok(n) => total += n, Err(_) => println!("Überspringe '{arg}': keine Zahl"), } } println!("Summe: {total}");}Ausgabe:
Überspringe 'x': keine ZahlSumme: 20Jedes Argument kommt als Text an, sogar "4". Deshalb muss jedes einzeln geparst werden. Ungültige Eingaben mit einer Meldung zu überspringen ist freundlicher als ein Absturz. Du könntest auch beim ersten ungültigen Argument mit return aufhören, wenn das besser zu deinem Programm passt.
Zusammenfassung
Abschnitt betitelt „Zusammenfassung“std::env::args().collect()liefert dir einenVec<String>mit den Kommandozeilenargumenten. Index 0 ist immer der Programmname.- Prüfe
args.len(), bevor du auf einen Index zugreifst. Dann löst ein fehlendes Argument keinen Panic aus. .parse()wandelt einString-Argument in eine Zahl um – genau wie anderswo auch.- Steck das Auswerten der Argumente in eine Funktion, die
&[String]bekommt. Dann kannst du sie mit einer selbst gebauten Liste ausprobieren. - Für mehr als ein paar einfache Argumente ist das Crate
clapdie übliche, bewährte Wahl.
Kurzer Check
Abschnitt betitelt „Kurzer Check“Kurzer Check
1. Was ist das erste Element des Vec, den du mit env::args().collect() bekommst?
2. Warum solltest du args.len() prüfen, bevor du auf einen Index in args zugreifst?
3. Was bietet dir das Crate clap, was der direkte Zugriff auf args nicht bietet?
Punkte: 0 / 3
Weiterlesen
Abschnitt betitelt „Weiterlesen“- Kommandozeilenargumente entgegennehmen – Das Rust-Buch: der Anfang des Projekts
minigrepaus dem Buch, das ein Suchwort und einen Dateinamen von der Kommandozeile liest. std::env::args– Standardbibliothek (auf Englisch): wasargs()genau zurückgibt und wann es einen Panic auslöst (bei Argumenten, die kein gültiges Unicode sind).- Program arguments – Rust by Example (auf Englisch): ein kurzes Beispiel und dazu ein Abschnitt über das Auswerten von Argumenten mit
match. - Command Line Applications in Rust (auf Englisch): ein kostenloses Mini-Buch über echte Kommandozeilen-Tools – vom Auswerten der Argumente bis zum Testen und Ausliefern.
- clap derive tutorial – docs.rs (auf Englisch): eine Schritt-für-Schritt-Anleitung zu Flags, Optionen, Unterbefehlen und Validierung mit
clap.