Rust使用clap库时如何保留命令行参数的原始输入顺序?
解决方案
针对你的需求,有两种稳定可用的方案,都不需要手动维护参数名列表,同时完全保留clap的所有能力(参数校验、自动补全、帮助生成、子命令支持等):
方案1:clap 4.4+ 原生迭代方案(推荐)
clap从4.4版本开始提供了occurrences()方法,可以直接按用户输入的原始顺序迭代所有传入的参数,无需额外处理:
use clap::{Arg, Command}; use std::ffi::OsStr; fn main() { // 正常定义你的所有参数、子命令逻辑,不需要做任何特殊修改 let matches = Command::new("mytool") .arg(Arg::new("foo").long("foo").takes_value(true)) .arg(Arg::new("bar").long("bar").takes_value(true)) // 你原本的其他参数、子命令定义都保留在这里 .get_matches(); // 直接按原始输入顺序获取所有参数 let mut ordered_args = Vec::new(); for occurrence in matches.occurrences() { let arg_id = occurrence.0.as_str(); let values: Vec<&OsStr> = occurrence.1.values().collect(); ordered_args.push((arg_id, values)); } // 后续按ordered_args的顺序处理即可 for (arg, vals) in ordered_args { println!("参数标识:{},输入值:{:?}", arg, vals); } }
输入mytool --foo this --bar that时,输出顺序为foo在前、bar在后;反过来输入则顺序相反,完全符合要求。
方案2:兼容clap 3.x/4.0~4.3版本的通用方案
如果你的项目使用的clap版本较低,没有内置occurrences()方法,可以通过参数索引自行排序实现,逻辑同样简单:
use clap::{Arg, Command}; fn main() { // 正常定义参数、子命令 let matches = Command::new("mytool") .arg(Arg::new("foo").long("foo").takes_value(true)) .arg(Arg::new("bar").long("bar").takes_value(true)) .get_matches(); let mut ordered_args = Vec::new(); // 遍历所有被用户传入的参数,无需手动指定参数名 for arg_id in matches.args.keys() { // 获取该参数在原始输入中的所有位置索引 if let Some(indices) = matches.indices_of(arg_id.as_str()) { // 获取该参数的所有输入值 if let Some(values) = matches.get_many::<std::ffi::OsString>(arg_id.as_str()) { // 索引与值一一对应,存入列表 for (idx, val) in indices.zip(values) { ordered_args.push((idx, arg_id.as_str(), val)); } } } } // 按原始输入的索引排序,得到输入顺序 ordered_args.sort_by_key(|item| item.0); // 后续处理逻辑 for (_, arg, val) in ordered_args { println!("参数标识:{},输入值:{:?}", arg, val); } }
注意事项
如果你的参数包含无值的Flag类型(比如--verbose这类开关参数),只需要在定义参数时指定action = clap::ArgAction::SetTrue即可,上述两种方案都可以正常识别到Flag的出现位置。
内容的提问来源于stack exchange,提问作者Andy Jewell
相关产品推荐
相关产品推荐

