问题

我试图以任何顺序解析一些命令行参数。其中两个是单值和强制性的,另一个是可选的逗号分隔列表:

usage:
 -mo <value1,value2,...,valueN>
 -sm1 <value>
 -sm2 <value>


使用任何旧的解析器(BasicParserPosixParserGnuParser),代码都可以正常工作,但是如果我改用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

谢谢你的帮助。

最佳答案

我认为这是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上阅读)。

不幸的是,由于最后一个条款false,对于总是多个字符的“短选项”,它总是返回options.hasShortOption(token.substring(1, 2))。它肯定会在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,因为方法是私有的,调用方法是私有的(isOptionisArgumenthandleToken),而DefaultParser依赖于Options中的程序包本地方法。

我测试修复程序的方法是将DefaultParser复制/粘贴到我的本地项目中,移到org.apache.commons.cli包中并进行上面的更改。



对于问题中的特定情况,您可以使用躲避方案来解决,您可以添加虚拟短选项"s",这会诱骗isShortOption(...)sm1和/或sm2选项返回true。像这样:

    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

10-04 20:35