Commons CLI 中的 DefaultParser 的行为与已弃用的解析器不同
DefaultParser in Commons CLI doesn't behave like the deprecated parsers
问题
我正在尝试以任何顺序解析一些命令行参数。其中两个是单值和强制性的,另一个是可选的逗号分隔列表:
usage:
-mo <value1,value2,...,valueN>
-sm1 <value>
-sm2 <value>
使用任何旧的解析器(BasicParser
、PosixParser
和 GnuParser
)代码工作正常,但如果我改用 DefaultParser
,MissingOptionException
被抛出。
代码
import java.util.Arrays;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
public class Foo {
public static void main(String[] args) throws Exception {
Option singleMandatory1 = Option.builder("sm1")
.argName("value")
.hasArg()
.required()
.build();
Option singleMandatory2 = Option.builder("sm2")
.argName("value")
.hasArg()
.required()
.build();
Option multipleOptional = Option.builder("mo")
.argName("value1,value2,...,valueN")
.hasArgs()
.valueSeparator(',')
.build();
Options options = new Options();
options.addOption(singleMandatory1);
options.addOption(singleMandatory2);
options.addOption(multipleOptional);
CommandLineParser parser = new DefaultParser();
CommandLine line = parser.parse(options, args);
for (Option o : line.getOptions()) {
System.out.println(o.getOpt() + '\t'
+ Arrays.toString(o.getValues()));
}
}
}
命令行参数
-sm1 Alice -sm2 Bob -mo Charles,David
有效
-sm1 Alice -mo Charles,David -sm2 Bob
只能使用旧的(现已弃用)解析器
我错过了什么吗?我正在使用 commons-cli-1.4-SNAPSHOT
.
感谢您的帮助。
使用 "mo" 选项,您使用的是 hasArgs()
方法而不是 hasArg()
。因此,在后一种情况下,-sm2
和 Bob
将被解析为 "mo" 选项的附加参数。当使用 hasArg()
时,该示例工作正常(您仍然可以将多个值传递给 "mo" 选项)
我认为这是 DefaultParser
中的错误。最终归结为这个方法:
/**
* Tells if the token looks like a short option.
*
* @param token
*/
private boolean isShortOption(String token)
{
// short options (-S, -SV, -S=V, -SV1=V2, -S1S2)
return token.startsWith("-") && token.length() >= 2 &&
options.hasShortOption(token.substring(1, 2));
}
(换行以便于在 SO 上阅读)。
不幸的是,由于最后的子句 options.hasShortOption(token.substring(1, 2))
,对于 "short options",这将始终 return false
,因为 "short options"。 它肯定会在 return 语句之前的评论中的第 2、3 和 4 项上失败,这让我相信这是一个错误。我可能误解了评论背后的意图,所以请忽略之前的声明。
修复可能如下所示:
/**
* Tells if the token looks like a short option.
*
* @param token
*/
private boolean isShortOption(String token)
{
// short options (-S, -SV, -S=V, -SV1=V2, -S1S2)
// extended to handle short options of more than one character
if (token.startsWith("-") && token.length() >= 2)
{
return options.hasShortOption(token.substring(1, 2)) ||
options.hasShortOption(extractShortOption(token));
}
return false;
}
/**
* Extract option from token. Assume the token starts with '-'.
*/
private String extractShortOption(String token)
{
int index = token.indexOf('=');
return (index == -1) ? token.substring(1) : token.substring(1, index);
}
不幸的是,没有很好的方法将其放入 DefaultParser
,因为方法是私有的,调用方法是私有的(isOption
、isArgument
和 handleToken
) DefaultParser
依赖于 Options
.
中的包本地方法
我测试修复的方法是 copy/plaster DefaultParser
进入我的本地项目,移动到 org.apache.commons.cli
包并进行上述更改。
作为针对问题中特定情况的狡猾解决方法,您可以添加一个虚拟短选项 "s"
,这会欺骗 isShortOption(...)
为 return 为真 sm1
and/or sm2
选项。像这样:
Option singleMandatory1 = Option.builder("sm1")
.argName("value")
.hasArg()
.required()
.build();
Option singleMandatory2 = Option.builder("sm2")
.argName("value")
.hasArg()
.required()
.build();
Option multipleOptional = Option.builder("mo")
.argName("value1,value2,...,valueN")
.hasArgs()
.valueSeparator(',')
.build();
Option dummyOptional = Option.builder("s")
.build();
Options options = new Options();
options.addOption(singleMandatory1);
options.addOption(singleMandatory2);
options.addOption(multipleOptional);
options.addOption(dummyOptional);
CommandLineParser parser = new DefaultParser();
CommandLine line = parser.parse(options, args);
ASF JIRA 上的这个问题似乎抓住了这个问题,尽管触发案例略有不同:https://issues.apache.org/jira/browse/CLI-265
问题
我正在尝试以任何顺序解析一些命令行参数。其中两个是单值和强制性的,另一个是可选的逗号分隔列表:
usage:
-mo <value1,value2,...,valueN>
-sm1 <value>
-sm2 <value>
使用任何旧的解析器(BasicParser
、PosixParser
和 GnuParser
)代码工作正常,但如果我改用 DefaultParser
,MissingOptionException
被抛出。
代码
import java.util.Arrays;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
public class Foo {
public static void main(String[] args) throws Exception {
Option singleMandatory1 = Option.builder("sm1")
.argName("value")
.hasArg()
.required()
.build();
Option singleMandatory2 = Option.builder("sm2")
.argName("value")
.hasArg()
.required()
.build();
Option multipleOptional = Option.builder("mo")
.argName("value1,value2,...,valueN")
.hasArgs()
.valueSeparator(',')
.build();
Options options = new Options();
options.addOption(singleMandatory1);
options.addOption(singleMandatory2);
options.addOption(multipleOptional);
CommandLineParser parser = new DefaultParser();
CommandLine line = parser.parse(options, args);
for (Option o : line.getOptions()) {
System.out.println(o.getOpt() + '\t'
+ Arrays.toString(o.getValues()));
}
}
}
命令行参数
-sm1 Alice -sm2 Bob -mo Charles,David
有效
-sm1 Alice -mo Charles,David -sm2 Bob
只能使用旧的(现已弃用)解析器
我错过了什么吗?我正在使用 commons-cli-1.4-SNAPSHOT
.
感谢您的帮助。
使用 "mo" 选项,您使用的是 hasArgs()
方法而不是 hasArg()
。因此,在后一种情况下,-sm2
和 Bob
将被解析为 "mo" 选项的附加参数。当使用 hasArg()
时,该示例工作正常(您仍然可以将多个值传递给 "mo" 选项)
我认为这是 DefaultParser
中的错误。最终归结为这个方法:
/**
* Tells if the token looks like a short option.
*
* @param token
*/
private boolean isShortOption(String token)
{
// short options (-S, -SV, -S=V, -SV1=V2, -S1S2)
return token.startsWith("-") && token.length() >= 2 &&
options.hasShortOption(token.substring(1, 2));
}
(换行以便于在 SO 上阅读)。
不幸的是,由于最后的子句 options.hasShortOption(token.substring(1, 2))
,对于 "short options",这将始终 return false
,因为 "short options"。 它肯定会在 return 语句之前的评论中的第 2、3 和 4 项上失败,这让我相信这是一个错误。我可能误解了评论背后的意图,所以请忽略之前的声明。
修复可能如下所示:
/**
* Tells if the token looks like a short option.
*
* @param token
*/
private boolean isShortOption(String token)
{
// short options (-S, -SV, -S=V, -SV1=V2, -S1S2)
// extended to handle short options of more than one character
if (token.startsWith("-") && token.length() >= 2)
{
return options.hasShortOption(token.substring(1, 2)) ||
options.hasShortOption(extractShortOption(token));
}
return false;
}
/**
* Extract option from token. Assume the token starts with '-'.
*/
private String extractShortOption(String token)
{
int index = token.indexOf('=');
return (index == -1) ? token.substring(1) : token.substring(1, index);
}
不幸的是,没有很好的方法将其放入 DefaultParser
,因为方法是私有的,调用方法是私有的(isOption
、isArgument
和 handleToken
) DefaultParser
依赖于 Options
.
我测试修复的方法是 copy/plaster DefaultParser
进入我的本地项目,移动到 org.apache.commons.cli
包并进行上述更改。
作为针对问题中特定情况的狡猾解决方法,您可以添加一个虚拟短选项 "s"
,这会欺骗 isShortOption(...)
为 return 为真 sm1
and/or sm2
选项。像这样:
Option singleMandatory1 = Option.builder("sm1")
.argName("value")
.hasArg()
.required()
.build();
Option singleMandatory2 = Option.builder("sm2")
.argName("value")
.hasArg()
.required()
.build();
Option multipleOptional = Option.builder("mo")
.argName("value1,value2,...,valueN")
.hasArgs()
.valueSeparator(',')
.build();
Option dummyOptional = Option.builder("s")
.build();
Options options = new Options();
options.addOption(singleMandatory1);
options.addOption(singleMandatory2);
options.addOption(multipleOptional);
options.addOption(dummyOptional);
CommandLineParser parser = new DefaultParser();
CommandLine line = parser.parse(options, args);
ASF JIRA 上的这个问题似乎抓住了这个问题,尽管触发案例略有不同:https://issues.apache.org/jira/browse/CLI-265