Rust by Example

49.2 getopts

To build unix-style command line interfaces, you can use the getopts crate.

Here is a simple implementation of the echo unix program:

#![feature(io)] #![feature(rustc_private)] #![feature(core)] #![feature(env)] #![feature(collections)] extern crate getopts; use std::env; use std::old_io::{print, println}; use std::old_io::stdio; static VERSION: &'static str = "1.0.0"; fn main() { let args: Vec<String> = env::args().map(|x| x.to_string()) .collect(); let ref program = args[0]; // Set possible flags. // The first argument to `optflag` is the short flag name. // The second argument is the long flag name. // The third argument is the help text. let opts = [ getopts::optflag("n", "", "do not output the trailing newline"), getopts::optflag("h", "help", "display this help and exit"), getopts::optflag("V", "version", "output version information and exit"), ]; let matches = match getopts::getopts(args.tail(), &opts) { Ok(m) => m, Err(f) => { println!("{}", f); env::set_exit_status(1); return; // The exit code is 0 (success) by default. // Any exit code other than 0 indicates failure. } }; if matches.opt_present("help") { //^ We could as well have used the short name: "h" println!("echo {} - display a line of text", VERSION); println!(""); println!("Usage:"); println!(" {} [SHORT-OPTION]... [STRING]...", program); println!(" {} LONG-OPTION", program); println!(""); println(getopts::usage("Echo the STRING(s) to standard output.", &opts) .as_slice()); return; } if matches.opt_present("version") { println!("echo version: {}", VERSION); return; } if !matches.free.is_empty() { //^ `matches.free` contains all the arguments that are not options. let string = matches.free.connect(" "); print(string.as_slice()); } if !matches.opt_present("n") { println!("") } else { stdio::flush(); } }
$ ./echo -h
echo 1.0.0 - display a line of text

Usage:
 ./echo [SHORT-OPTION]... [STRING]...
 ./echo LONG-OPTION

Echo the STRING(s) to standard output.

Options:
    -n                  do not output the trailing newline
    -h --help           display this help and exit
    -V --version        output version information and exit

$ ./echo --version
echo version: 1.0.0
$ ./echo Hello, World!
Hello, World!

This is a simplified version of the echo implementation by uutils.

It is also possible to use options instead of flags, such that values can be passed to the program:

// testopt.rs
#![feature(rustc_private)]
#![feature(collections)]
#![feature(env)]

extern crate getopts;

use std::env;

fn main() {
    let args: Vec<String> = env::args().map(|x| x.to_string())
                                       .collect();

    let opts = [
        getopts::optflag("a", "long_a", ""),
        getopts::optflag("b", "long_b", ""),
        getopts::optopt("c", "long_c", "", "VALUE"),
        //^ Use `optflagopt` if the argument should be optional.
        //  Use `reqopt` if the option is required.
        //  Use `optmulti`, `optflagmulti` if options can occur multiple times.
    ];

    let matches = match getopts::getopts(args.tail(), &opts) {
        Ok(m) => m,
        Err(f) => {
            println!("{}", f);
            env::set_exit_status(1);
            return;
        }
    };
    let a = if matches.opt_present("a") {true} else {false};
    let b = if matches.opt_present("b") {true} else {false};
    let c = match matches.opt_str("c") {
        Some(s) => s,
        None => String::from_str(""),
    };
    //^ Use `matches.opt_default` if you need a default (`opflagopt`).
    //  Use `matches.opt_count` if you need to count how many were matched
    //  (`*multi`).

    println!("a={}, b={}, c=\"{}\"", a, b, c);
    if !matches.free.is_empty() {
        println!("free arguments: {:?}", matches.free);
    }
}

Here are some examples how the program behaves given different combinations of arguments:

$ ./testopt
a=false, b=false, c=""
$ ./testopt -a -b
a=true, b=true, c=""
$ ./testopt -ab
a=true, b=true, c=""
$ ./testopt -c
Argument to option 'c' missing.
$ ./testopt -c value
a=false, b=false, c="value"
$ ./testopt -c=value
a=false, b=false, c="=value"
$ ./testopt -cvalue
a=false, b=false, c="value"
$ ./testopt arg
a=false, b=false, c=""
free arguments: [arg]
$ ./testopt -a arg
a=true, b=false, c=""
free arguments: [arg]
$ ./testopt -c value arg
a=false, b=false, c="value"
free arguments: [arg]
$ ./testopt -a -- -b
a=true, b=false, c=""
free arguments: [-b]
$ ./testopt -a -
a=true, b=false, c=""
free arguments: [-]