6 个版本
0.1.5 | 2021年5月27日 |
---|---|
0.1.4 | 2021年5月2日 |
0.1.3 | 2021年4月25日 |
#961 in 命令行界面
在 rout 中使用
110KB
2.5K SLoC
ap - 参数解析器
概述
ap
是一个用于解析命令行参数的 Rust 包(crate)。
它体积小、简单,但也有一些有趣的功能。
详细说明
请参阅[此处](https://docs.rs/ap)的文档。
lib.rs
:
简单的命令行参数解析 crate。
如果您需要更多功能,请考虑使用优秀的 clap
crate。
要了解“简单”的含义,请参阅限制部分。
目录
概述
此 crate 用于解析命令行参数。它为每个解析的已注册选项调用一个处理函数。
快速入门
注意:如果您不熟悉命令行处理,请参阅术语部分。
-
创建一个表示将用于处理 所有 选项的处理器的
struct
类型。如果只想在特定选项指定时收到通知,则该结构可以为空;或者您可以指定成员以记录或计算特定细节。
#[derive(Clone, Debug, Default)] struct MyHandler {}
-
为该
struct
实现一个 [Handler] 特性。此特性仅要求您创建一个返回 [Result] 的单个
handle()
方法,以指示成功或失败。# # impl Handler for &mut MyHandler { fn handle(&mut self, arg: Arg) -> Result<()> { // ... Ok(()) } }
-
为您的
struct
类型创建一个处理程序变量。# # # # let mut handler = MyHandler::default();
-
创建一个 [Args] 变量以保存您希望支持的 所有 参数。
# let mut args = Args::default();
-
将您希望支持的每个参数添加到 [Args] 变量中的新 [Arg]。
至少,您必须指定参数的“名称”(单字符短选项值)。
默认情况下,选项是“标志”(请参阅术语部分)。
# # // Support "-a <value>" option. args.add(Arg::new('a').needs(Need::Argument)); // Support "-d" flag option. args.add(Arg::new('d'));
-
创建一个表示程序的变量 [App],并指定 [Args] 和 [Handler] 变量
# # # # # # # let mut args = App::new("my app") .help("some text") .args(args) .handler(Box::new(&mut handler));
-
在 [App] 变量上调用
parse()
方法。对于添加到 [Args] 变量的所有 [Arg] 参数,都会调用处理器# # # # # # # # // Parse the command-line let result = args.parse();
示例
以下是一个完整的示例,展示了如何编写支持一些命令行选项的程序。它还展示了处理器如何修改其状态,从而实现具有状态和条件选项处理。
use ap::{App, Arg, Args, Handler, Need, Result};
// The type that will be used to handle all the CLI options
// for this program.
#[derive(Clone, Debug, Default)]
struct MyHandler {
i: usize,
v: Vec<String>,
s: String,
}
impl Handler for &mut MyHandler {
fn handle(&mut self, arg: Arg) -> Result<()> {
println!(
"option: {:?}, value: {:?}, count: {}",
arg.option, arg.value, arg.count
);
// Change behaviour if user specified '-d'
if arg.option == 'd' {
self.i += 7;
} else {
self.i += 123;
}
self.s = "string value set by handler".into();
self.v.push("vector modified by handler".into());
Ok(())
}
}
fn main() -> Result<()> {
let mut handler = MyHandler::default();
println!("Initial state of handler: {:?}", handler);
let mut args = Args::default();
// Support "-a <value>" option.
args.add(Arg::new('a').needs(Need::Argument));
// Support "-b <value>" option.
args.add(Arg::new('b').needs(Need::Argument));
// Support "-d" flag option.
args.add(Arg::new('d'));
let mut args = App::new("my app")
.help("some text")
.args(args)
.handler(Box::new(&mut handler));
// Parse the command-line
let result = args.parse();
// If you want to inspect the handler after parsing, you need to
// force ownership to be returned by dropping the `Args` variable.
drop(args);
println!("Final state of handler: {:?}", handler);
// return value
result
}
对于更多示例,请尝试 examples/
目录中的程序
$ cargo run --example simple -- -a foo -d -a bar -d -a baz
$ cargo run --example positional-args-only -- one two "hello world" three "foo bar" four "the end"
$ cargo run --example option-and-positional-args -- "posn 1" -d "posn 2" -a "hello world" -a "foo bar" "the end" -d
$ cargo run --example error-handler -- -a -e -i -o -u
细节
术语
注意:有关更多详细信息,请参阅
getopt(3)
。
-
“参数”是指在命令行上传递给程序的值。
参数可以是“选项”或“位置参数”。
注意:单引号或双引号内的字符串被视为一个参数,即使该字符串包含多个单词(这种魔法由 shell 处理)。
-
“选项”是以下划线字符(
-
)开头并以单个字符结尾的参数,该字符本身不是-
,例如,-a
、-z
、-A
、-Z
、-0
、-9
、等等。该字符是选项的“名称”。选项名称区分大小写:大写和小写字母代表不同的选项。
这种类型的选项被称为“短选项”,因为它只由一个字符识别。
-
接受参数(一个称为“选项参数”或“optarg”的值)的选项通常简单地称为“选项”,因为这是最常见的选项形式。
-
“选项参数”是紧跟在选项后面的值。它被认为是“绑定”或与前面的选项配对。根据定义,选项参数不能以下划线开头,以避免被视为自己的选项。
-
不接受参数的选项称为“标志”或“独立选项”。这些通常用于切换某些功能的开启或关闭。
标志示例
大多数程序支持一些常见的标志
-h
:显示帮助/使用说明并退出。-v
:显示版本号并退出,或有时启用详细模式。
-
“位置参数”(也称为“非选项参数”)不是选项的参数:它是一个单词或一个引号内的字符串(该字符串不能以下划线开头,除非它被转义为
\-
)。位置参数示例
echo(1)
是处理位置参数的程序的良例$ echo one two three "hello, world" four five "the end"
-
特殊选项
--
保留用于表示“所有选项的结束”:它可以由需要接受一组选项后跟一组位置参数的程序使用。即使双横线后的参数以下划线开头,它也不会被视为选项。如果该包在命令行上找到
--
,它将停止处理命令行参数。
参数类型示例
假设一个程序按照以下方式运行:
$ myprog -d 371 "hello, world" -x "awesome value" -v "the end"
该程序有7个实际的命令行参数。
1: '-d'
2: '371'
3: 'hello, world'
4: '-x'
5: 'awesome value'
6: '-v'
7: 'the end'
这些参数的解析方式取决于每个选项(以-
开头的参数)是否指定了取值。
如果所有选项都指定为标志,则参数的解析方式如下:
'-d' # A flag option.
'371' # A positional argument.
'hello, world' # A positional argument.
'-x' # A flag option.
'awesome value' # A positional argument.
'-v' # A flag option.
'the end' # A positional argument.
但是,如果我们假设-d
和-x
被指定为取值,那么参数分组如下:
'-d 371' # An option ('d') with a numeric option argument ('371').
'hello, world' # A positional argument ('hello, world').
'-x awesome value' # An option ('x') with a string option argument ('awesome value').
'-v' # A flag option.
'the end' # A positional argument.
或者,如果我们假设所有选项都取值,那么参数分组如下:
'-d 371' # An option ('d') with a numeric option argument ('371').
'hello, world' # A positional argument ('hello, world').
'-x 'awesome value'' # An option ('x') with a string option argument ('awesome value').
'-v 'the end'' # An option('v') with a string option argument ('the end').
处理歧义
默认情况下,使用getopt(3)
语义,这意味着在解析参数时没有歧义:如果声明了一个[Arg],指定了Need::Argument,则选项参数之后的下一个参数(无论是什么!)将被消耗并用作选项参数。
尽管没有歧义,但这对用户来说可能是个意外,因为其他(通常是较新的)命令行参数解析器以微妙不同的方式工作。例如,假设程序指定了以下内容:
#
let mut args = Args::default();
args.add(Arg::new('1'));
args.add(Arg::new('2').needs(Need::Argument));
如果程序随后按照以下方式调用...
$ prog -2 -1
...则-2
选项的值将被设置为-1
,而-1
选项将假定未指定。这就是getopt(3)
的工作方式。然而,这可能不是用户所期望的,或者程序员所希望的。
在解析上述命令行时,另一种替代策略是将选项视为比参数更重要,并在这种情况下报错,因为-2
选项未提供参数(因为-1
选项在有效选项参数之前指定了)。
有关此细微之处的更多详细信息,请参阅[App]或[Settings]的no_strict_options()
方法。
原因
为什么还需要另一个命令行解析器呢?
有很多Rust命令行参数解析器。这个是写的,因为我找不到一个满足以下所有要求的crate:
-
允许选项和非选项(“位置参数”)混合。
这对于某些用例非常有用,并且在符合POSIX规范的
libc
实现中是标准库调用。引用自getopt(3)
:如果
optstring
的第一个字符是'-
',则每个非选项argv
元素都处理为具有字符码1
的选项的参数。 -
按顺序解析命令行参数。
现代时尚似乎是构建选项的哈希表,以便程序可以查询是否指定了选项。这在大多数情况下很有用,但我有一个需要顺序和每个选项出现的次数重要的用例。
-
允许指定一个处理函数来处理遇到的参数。
总结来说,我需要一个更类似POSIX的(POSIXLY_CORRECT
)命令行参数解析器,所以,这就是它。
功能和行为的总结
-
简单直观(“人体工程学”)的API。
-
代码库小。
-
全面的单元测试集。
-
按严格顺序解析参数。
-
立即处理每个参数。
一旦遇到(注册和有效的)参数,就会调用处理程序。
-
参数不会被重新排序。
-
需要指定一个函数来处理每个选项。
-
选项参数始终以字符串形式返回。
调用者可以根据需要将它们转换为数字等。
-
允许标志选项、带有参数的选项和“位置参数”(非选项参数)混合使用。
-
允许多次指定选项(并记录该次数)。
注意:您可以通过在处理程序中检查
Arg.count
值来限制发生次数。 -
可以将选项定义为必选。
-
可以配置未知选项为忽略或传递给处理程序。
注意
- 默认情况下,未知选项不会被处理,如果找到未知选项,将生成错误。
- 如果您想支持位置参数,可以注册一个[Arg]用于[POSITIONAL_HANDLER_OPT],或者设置
Settings.ignore_unknown_options
。
-
“未知”的位置参数可以配置为忽略或传递给处理程序。
注意
- 默认情况下,位置参数不会被处理,如果找到位置参数,将生成错误。
- 如果您想支持位置参数,可以注册一个[Arg]用于[POSITIONAL_HANDLER_OPT],或者设置
Settings.ignore_unknown_posn_args
。
-
自动生成帮助/用法说明(
-h
)。
限制
-
不支持选项捆绑
示例:
-d -v -a "foo bar"
是有效的,但-dva "foo bar"
是无效的。 -
不支持非ASCII选项名称。
-
不支持长选项
示例:
-v
是有效的,但--verbose
是无效的。 -
选项及其参数必须由空白分隔。
示例: '
-d 3
' 是有效的,但 '-d3
' 是有效的。 -
不支持带有可选参数的选项。
说明:选项必须定义为标志(无参数)或标准选项(需要值)。不能同时是两者。
-
选项不能接受多个值
示例:
-a "foo bar" "baz" "the end"
不能解析为一个单独的-a
选项。然而
- 选项可以指定多次,每次具有不同的值。
- 您可以使用 [POSITIONAL_HANDLER_OPT] 解析该命令行。
依赖关系
~305–770KB
~18K SLoC