5. 选项
命令行参数可以分为选项和位置参数。 以下各节介绍了功能、如何定义和使用选项。
5.1. 定义
选项可以在目标方法中定义为方法参数中的注释
或以编程方式使用 。CommandRegistration
具有带参数的目标方法会自动注册到匹配的 参数名称。
public String example(String arg1) {
return "Hello " + arg1;
}
@ShellOption
annotation 可用于定义选项名称,如果您
不希望它与 argument name 相同。
public String example(@ShellOption(value = { "--argx" }) String arg1) {
return "Hello " + arg1;
}
如果定义选项名称时不带前缀,则会发现 或 。
从 ShellMethod#前缀。-
--
public String example(@ShellOption(value = { "argx" }) String arg1) {
return "Hello " + arg1;
}
编程方式 with 是使用方法添加一个长名称。CommandRegistration
CommandRegistration.builder()
.withOption()
.longNames("arg1")
.and()
.build();
5.2. 短格式
短格式 POSIX 选项在大多数情况下只是长格式的同义词,但
添加了其他功能以将这些选项组合在一起。拥有短
选项 A、B、C 可用作 。-abc
编程短选项是使用短名称函数定义的。
CommandRegistration.builder()
.withOption()
.shortNames('a')
.and()
.withOption()
.shortNames('b')
.and()
.withOption()
.shortNames('c')
.and()
.build();
如果将 type 定义为标志,则具有组合格式的 Short 选项非常有用
这意味着 type 是一个布尔值。这样你就可以定义 flag 的前提
as 、 或 .-abc
-abc true
-abc false
CommandRegistration.builder()
.withOption()
.shortNames('a')
.type(boolean.class)
.and()
.withOption()
.shortNames('b')
.type(boolean.class)
.and()
.withOption()
.shortNames('c')
.type(boolean.class)
.and()
.build();
使用 annotation model,您可以直接定义短参数。
public String example(
@ShellOption(value = { "-a" }) String arg1,
@ShellOption(value = { "-b" }) String arg2,
@ShellOption(value = { "-c" }) String arg3
) {
return "Hello " + arg1;
}
5.3. Arity
有时,您希望通过选项对参数数量进行更精细的控制 在解析操作发生时进行处理。Arity 定义为最小值和最大值 值,其中 min 必须为零或正整数,max 必须大于或等于 min。
CommandRegistration.builder()
.withOption()
.longNames("arg1")
.arity(0, 1)
.and()
.build();
Arity 也可以定义为枚举,它们是快捷方式
如下表 OptionArity 所示。OptionArity
CommandRegistration.builder()
.withOption()
.longNames("arg1")
.arity(OptionArity.EXACTLY_ONE)
.and()
.build();
值 | 最小/最大 |
---|---|
零 |
0 / 0 |
ZERO_OR_ONE |
0 / 1 |
EXACTLY_ONE |
1 / 1 |
ZERO_OR_MORE |
0 / 整数最大值 |
ONE_OR_MORE |
1 / 整数 MAX |
注释模型仅支持定义 arity 的最大值。
public String example(@ShellOption(arity = 1) String arg1) {
return "Hello " + arg1;
}
手动定义 arity 的一个用例是施加限制 many parameters 选项接受。
CommandRegistration.builder()
.command("arity-errors")
.withOption()
.longNames("arg1")
.type(String[].class)
.required()
.arity(1, 2)
.and()
.withTarget()
.function(ctx -> {
String[] arg1 = ctx.getOptionValue("arg1");
return "Hello " + Arrays.asList(arg1);
})
.and()
.build();
在上面的示例中,我们有选项 arg1,它被定义为类型 String[]。阿尔蒂 定义它至少需要 1 个参数,而不是超过 2 个参数。如下所示 引发特殊异常 TooManyArgumentsOptionException 和 NotEnoughArgumentsOptionException 以指示 arity 不匹配。
shell:>e2e reg arity-errors --arg1
Not enough arguments --arg1 requires at least 1.
shell:>e2e reg arity-errors --arg1 one
Hello [one]
shell:>e2e reg arity-errors --arg1 one two
Hello [one, two]
shell:>e2e reg arity-errors --arg1 one two three
Too many arguments --arg1 requires at most 2.
5.4. 位置
位置信息主要与命令目标方法相关:
CommandRegistration.builder()
.withOption()
.longNames("arg1")
.position(0)
.and()
.build();
小心位置参数,因为它可能很快就会出现 使这些映射到的选项变得混乱。 |
通常,当参数在 命令行,无论是 long 选项还是 short 选项。一般而言 有 options、option arguments 和 arguments,其中后者 是未映射到任何 spesific 选项的 sit。
然后,无法识别的参数可以具有辅助映射逻辑,其中 位置信息很重要。使用期权头寸,您是 本质上是告诉命令解析如何解释普通 RAW 模棱两可的论点。
让我们看看当我们不定义位置时会发生什么。
CommandRegistration.builder()
.command("arity-strings-1")
.withOption()
.longNames("arg1")
.required()
.type(String[].class)
.arity(0, 2)
.and()
.withTarget()
.function(ctx -> {
String[] arg1 = ctx.getOptionValue("arg1");
return "Hello " + Arrays.asList(arg1);
})
.and()
.build();
选项 arg1 是必需的,并且没有信息如何处理参数导致缺少选项的错误。one
shell:>arity-strings-1 one
Missing mandatory option --arg1.
现在让我们定义一个位置 。0
CommandRegistration.builder()
.command("arity-strings-2")
.withOption()
.longNames("arg1")
.required()
.type(String[].class)
.arity(0, 2)
.position(0)
.and()
.withTarget()
.function(ctx -> {
String[] arg1 = ctx.getOptionValue("arg1");
return "Hello " + Arrays.asList(arg1);
})
.and()
.build();
处理参数,直到我们得到最多 2 个参数。
shell:>arity-strings-2 one
Hello [one]
shell:>arity-strings-2 one two
Hello [one, two]
shell:>arity-strings-2 one two three
Hello [one, two]
5.5. 可选值
选项是必需的或不是,一般来说,它的行为方式取决于 命令目标:
CommandRegistration.builder()
.withOption()
.longNames("arg1")
.required()
.and()
.build();
在 Comments 模型中,没有直接的方法来定义 argument 是否为
自选。相反,它被指示为:NULL
public String example(
@ShellOption(defaultValue = ShellOption.NULL) String arg1
) {
return "Hello " + arg1;
}
5.6. 默认值
选项的默认值与可选值有些关系,因为在某些情况下,您 可能想知道用户是否定义了选项并更改了行为 基于默认值:
CommandRegistration.builder()
.withOption()
.longNames("arg1")
.defaultValue("defaultValue")
.and()
.build();
注解模型还支持定义默认值:
public String example(
@ShellOption(defaultValue = "defaultValue") String arg1
) {
return "Hello " + arg1;
}
5.7. 验证
Spring Shell 与 Bean 验证 API 集成以支持 命令参数的自动和自记录约束。
在命令参数上找到的注释和方法级别的注释是 honoured 并在命令执行之前触发验证。请考虑以下命令:
@ShellMethod("Change password.")
public String changePassword(@Size(min = 8, max = 40) String password) {
return "Password successfully set to " + password;
}
从前面的示例中,您可以免费获得以下行为:
shell:>change-password hello The following constraints were not met: --password string : size must be between 8 and 40 (You passed 'hello')
5.8. 标签
Option Label 在 shell 本身中没有其他功能行为,除了
默认命令输出什么。在命令文档中
一种类型的 an option 被记录下来,但这并不总是非常有用。因此
您可能希望为选项提供更好的描述性词。help
CommandRegistration.builder()
.withOption()
.longNames("arg1")
.and()
.withOption()
.longNames("arg2")
.label("MYLABEL")
.and()
.build();
然后,定义标签如 所示。help
my-shell:>help mycommand
NAME
mycommand -
SYNOPSIS
mycommand --arg1 String --arg2 MYLABEL
OPTIONS
--arg1 String
[Optional]
--arg2 MYLABEL
[Optional]
5.9. 类型
本节讨论如何将特定数据类型用作选项值。
5.9.1. 字符串
String
是最简单的类型,因为不涉及转换,因为
来自 User 的 inin 始终是一个字符串。
String example(@ShellOption(value = "arg1") String arg1) {
return "Hello " + arg1;
}
虽然并不严格要求将 type 定义为 a,但它始终是
建议这样做。String
CommandRegistration.builder()
.command("example")
.withOption()
.longNames("arg1")
.type(String.class)
.required()
.and()
.withTarget()
.function(ctx -> {
String arg1 = ctx.getOptionValue("arg1");
return "Hello " + arg1;
})
.and()
.build();
5.9.2. 布尔值
使用布尔类型稍微复杂一些,因为 there 和 where 后者可以是 null。布尔类型通常用作
表示参数值的标志可能不需要。boolean
Boolean
String example(
@ShellOption() boolean arg1,
@ShellOption(defaultValue = "true") boolean arg2,
@ShellOption(defaultValue = "false") boolean arg3,
@ShellOption() Boolean arg4,
@ShellOption(defaultValue = "true") Boolean arg5,
@ShellOption(defaultValue = "false") Boolean arg6
) {
return String.format("arg1=%s arg2=%s arg3=%s arg4=%s arg5=%s arg6=%s",
arg1, arg2, arg3, arg4, arg5, arg6);
}
shell:>example
arg1=false arg2=true arg3=false arg4=false arg5=true arg6=false
shell:>example --arg4
arg1=false arg2=true arg3=false arg4=true arg5=true arg6=false
shell:>example --arg4 false
arg1=false arg2=true arg3=false arg4=false arg5=true arg6=false
CommandRegistration.builder()
.command("example")
.withOption()
.longNames("arg1").type(boolean.class).and()
.withOption()
.longNames("arg2").type(boolean.class).defaultValue("true").and()
.withOption()
.longNames("arg3").type(boolean.class).defaultValue("false").and()
.withOption()
.longNames("arg4").type(Boolean.class).and()
.withOption()
.longNames("arg5").type(Boolean.class).defaultValue("true").and()
.withOption()
.longNames("arg6").type(Boolean.class).defaultValue("false").and()
.withTarget()
.function(ctx -> {
boolean arg1 = ctx.hasMappedOption("arg1")
? ctx.getOptionValue("arg1")
: false;
boolean arg2 = ctx.getOptionValue("arg2");
boolean arg3 = ctx.getOptionValue("arg3");
Boolean arg4 = ctx.getOptionValue("arg4");
Boolean arg5 = ctx.getOptionValue("arg5");
Boolean arg6 = ctx.getOptionValue("arg6");
return String.format("Hello arg1=%s arg2=%s arg3=%s arg4=%s arg5=%s arg6=%s",
arg1, arg2, arg3, arg4, arg5, arg6);
})
.and()
.build();
shell:>example
arg1=false arg2=true arg3=false arg4=null arg5=true arg6=false
shell:>example --arg4
arg1=false arg2=true arg3=false arg4=true arg5=true arg6=false
shell:>example --arg4 false
arg1=false arg2=true arg3=false arg4=false arg5=true arg6=false
5.9.3. 数字
数字按原样转换。
String example(@ShellOption(value = "arg1") int arg1) {
return "Hello " + arg1;
}
CommandRegistration.builder()
.command("example")
.withOption()
.longNames("arg1")
.type(int.class)
.required()
.and()
.withTarget()
.function(ctx -> {
boolean arg1 = ctx.getOptionValue("arg1");
return "Hello " + arg1;
})
.and()
.build();
5.9.4. 枚举
如果给定的值与 enum 本身完全匹配,则可以转换为 enum。 目前,您可以转换不区分大小写的假设。
enum OptionTypeEnum {
ONE,TWO,THREE
}
String example(@ShellOption(value = "arg1") OptionTypeEnum arg1) {
return "Hello " + arg1;
}
CommandRegistration.builder()
.command("example")
.withOption()
.longNames("arg1")
.type(OptionTypeEnum.class)
.required()
.and()
.withTarget()
.function(ctx -> {
OptionTypeEnum arg1 = ctx.getOptionValue("arg1");
return "Hello " + arg1;
})
.and()
.build();
5.9.5. 数组
数组可以按原样与字符串和基元类型一起使用。
String example(@ShellOption(value = "arg1") String[] arg1) {
return "Hello " + arg1;
}
CommandRegistration.builder()
.command("example")
.withOption()
.longNames("arg1")
.type(String[].class)
.required()
.and()
.withTarget()
.function(ctx -> {
String[] arg1 = ctx.getOptionValue("arg1");
return "Hello " + arg1;
})
.and()
.build();
using-shell-options.adoc 中未解决的指令 - include::using-shell-options-naming.adoc[]