Rust のコマンドラインオプション解析色々
本エントリは Rust その2 Advent Calendar 2016 - Qiita の 10 日目の記事です。
本記事では、Rust を使って CLI アプリケーションを作成してきた過程で得られた知見のひとつとして、コマンドラインオプション解析に用いる crate を簡単にまとめたいと思います。
基本事項
コマンドライン引数は std::env
モジュールの args()
(または args_os()
)を用いて取得します。
最初の要素には通常実行ファイルのパスが格納され、引数自体は2番目以降に格納されます。
use std::env; fn main() { let program: String = env::args().next().unwrap(); let args: Vec<String> = env::args().skip(1).collect(); }
getopts
getopts
は公式の提供しているコマンドライン解析用の crate です。
使い勝手としては Python の argparse が近いと思います。
getopts
の特長として、必要最小限の機能しか提供していないためシンプルであり、導入における敷居が低いことが挙げられます。
そのため、単純なオプションを解析を行う場合にはこれで十分事足りると思います。
使い方は以下のようになります(公式ドキュメントの例を一部修正)。
extern crate getopts; use std::{env, process}; use getopts::Options; #[derive(Debug)] struct Args { input: Vec<String>, output: Option<String>, // ... } fn print_usage(program: &str, opts: &Options) { let brief = format!("Usage: {} FILE [options]", program); print!("{}", opts.usage(&brief)); process::exit(0); } fn parse_args() -> Args { let args: Vec<String> = env::args().collect(); let program = args[0].clone(); let mut opts = Options::new(); opts.optopt("o", "", "set output file name", "NAME"); opts.optflag("h", "help", "print this help menu"); // ... let matches = opts.parse(&args[1..]) .unwrap_or_else(|f| panic!(f.to_string())); if matches.opt_present("h") { print_usage(&program, &opts); } if matches.free.is_empty() { print_usage(&program, &opts); } Args { input: matches.free.clone(), output: matches.opt_str("o"), // ... } } fn main() { let args = parse_args(); println!("{:?}", args); }
docopt.rs
docopt.rs
はドキュメント文字列を元にオプション解析器を生成する crate です。
多言語にも同名のパッケージがあると思いますが、それらと同じ感覚で使用することが出来ます。
docopt.rs
特有の機能として、RustcDecodable
を実装した構造体を用いてオプションの解析結果を
受け取ることができます。
また、nightly
限定ですがドキュメントを解析して解析結果を受け取る構造体を自動生成する docopt!
という
マクロを使用することでオプション解析を簡略化することが出来ます(僕は stable
派なので使ったことないですが…)。
使用例は以下の通りです。
extern crate docopt; extern crate rustc_serialize; use docopt::Docopt; const USAGE: &'static str = r" JSON version of xargs Usage: jsonargs [--parallel] <name> [<args>...] jsonargs (-h | --help) Options: -h --help Show this message. --parallel Run each command parallel "; #[derive(Debug, RustcDecodable)] struct Args { flag_parallel: bool, arg_name: String, arg_args: Vec<String>, } fn main() { let args = Docopt::new(USAGE) .and_then(|opt| opt.decode()) .unwrap_or_else(|e| e.exit()); let Args { arg_name: name, arg_args: args, flag_parallel: parallel } = args; // ... }
docopt.rs
の詳細な使用方法ははすでに良質な解説記事がありますのでそちらもご参照ください。
clap.rs
getopts
や docopt.rs
はどちらかと言うと小規模なアプリケーション向けのパーサであり、
複数のサブコマンドを用いるなど複雑なオプションが必要なアプリケーション向きでは無いです。
複雑なコマンドラインオプションの解析には clap.rs
を用いることが出来ます。
clap
は rustup
でも用いられているコマンドラインパーサであり、
公式リポジトリで紹介されている機能一覧を見てもわかるように
とにかく高機能です(語彙力)。
とりあえず気になるものだけ列挙してみると、次のような機能があります。
使用例は以下の通りです。
extern crate clap; use clap::{App, AppSettings, Arg}; fn build_app() -> clap::App<'static, 'static> { let program = std::env::args() .nth(0) .and_then(|s| { std::path::PathBuf::from(s) .file_stem() .map(|s| s.to_string_lossy().into_owned()) }) .unwrap(); App::new(program) .about("find files") .version("0.0.1") .author("Author name <author@example.com>") .setting(AppSettings::VersionlessSubcommands) .arg(Arg::from_usage("-i --ignore=[IGNORE] 'Ignored pattern'")) .arg(Arg::from_usage("-m --matches=[MATCHES] 'Pattern to match'")) .arg(Arg::from_usage("-a --absolute 'Show absolute path'")) .arg(Arg::from_usage("-d --directory 'Show only directories'")) .arg(Arg::from_usage("-A --async 'Search asynchronously'")) .arg(Arg::from_usage("-M --max-items=[N] 'Limit of displayed items'")) // ... } fn main() { let matches = build_app().get_matches(); let matchre : Option<&str> = matches.value_of("matches"); let ignore: Option<&str> = matches.value_of("ignore"); // ...、 }
個人的に便利だと思ったのが、各種シェル用の補完関数を自動生成する機能です。 例えば Zsh 用の補完スクリプトを生成する場合は次のようにすることで可能です。
use clap::Shell; let mut file = std::fs::OpenOptions::new() .write(true) .create(true) .open(concat!("_", env!("CARGO_PKG_NAME"))) .unwrap(); build_app().gen_completions_to(env!("CARGO_PKG_NAME"), Shell::Zsh, &mut file);
おわりに
以上、Rust でコマンドラインオプションの解析に使える crate の紹介でした。