简体   繁体   English

解析 Java 命令行程序的参数

[英]Parsing arguments to a Java command line program

What if I wanted to parse this:如果我想解析这个怎么办:

java MyProgram -r opt1 -S opt2 arg1 arg2 arg3 arg4 --test -A opt3

And the result I want in my program is:我在我的程序中想要的结果是:

regular Java args[]  of size=4
org.apache.commons.cli.Options[]  of size=3
org.apache.commons.cli.Options[] #2 of size=1

I would prefer to use Apache Commons CLI , but the documentation is a little unclear about the case I present above.我更喜欢使用Apache Commons CLI ,但文档对我上面介绍的案例有点不清楚。 Specifically, the documentation doesn't tell you how to handle options of the 3rd type I specify below:具体来说,文档没有告诉您如何处理我在下面指定的第三种类型的选项:

 1. options with a "-" char 2. options with a "--" char 3. options without any marker, or "bare args"

I wish that Apache Commons CLI would work but STILL be able to pass regular args to the program if those args didn't have a option prefix.我希望 Apache Commons CLI 能够工作,但如果这些 args 没有选项前缀,仍然能够将常规 args 传递给程序。 Maybe it does but the documentation doesnt say so as I read through it...也许确实如此,但当我通读它时,文档并没有这么说......

Use the Apache Commons CLI library commandline.getArgs() to get arg1, arg2, arg3, and arg4.使用Apache Commons CLI 库commandline.getArgs() 获取 arg1、arg2、arg3 和 arg4。 Here is some code:这是一些代码:



    import org.apache.commons.cli.CommandLine;
    import org.apache.commons.cli.Option;
    import org.apache.commons.cli.Options;
    import org.apache.commons.cli.Option.Builder;
    import org.apache.commons.cli.CommandLineParser;
    import org.apache.commons.cli.DefaultParser;
    import org.apache.commons.cli.ParseException;

    public static void main(String[] parameters)
    {
        CommandLine commandLine;
        Option option_A = Option.builder("A")
            .required(true)
            .desc("The A option")
            .longOpt("opt3")
            .build();
        Option option_r = Option.builder("r")
            .required(true)
            .desc("The r option")
            .longOpt("opt1")
            .build();
        Option option_S = Option.builder("S")
            .required(true)
            .desc("The S option")
            .longOpt("opt2")
            .build();
        Option option_test = Option.builder()
            .required(true)
            .desc("The test option")
            .longOpt("test")
            .build();
        Options options = new Options();
        CommandLineParser parser = new DefaultParser();

        String[] testArgs =
        { "-r", "opt1", "-S", "opt2", "arg1", "arg2",
          "arg3", "arg4", "--test", "-A", "opt3", };

        options.addOption(option_A);
        options.addOption(option_r);
        options.addOption(option_S);
        options.addOption(option_test);

        try
        {
            commandLine = parser.parse(options, testArgs);

            if (commandLine.hasOption("A"))
            {
                System.out.print("Option A is present.  The value is: ");
                System.out.println(commandLine.getOptionValue("A"));
            }

            if (commandLine.hasOption("r"))
            {
                System.out.print("Option r is present.  The value is: ");
                System.out.println(commandLine.getOptionValue("r"));
            }

            if (commandLine.hasOption("S"))
            {
                System.out.print("Option S is present.  The value is: ");
                System.out.println(commandLine.getOptionValue("S"));
            }

            if (commandLine.hasOption("test"))
            {
                System.out.println("Option test is present.  This is a flag option.");
            }

            {
                String[] remainder = commandLine.getArgs();
                System.out.print("Remaining arguments: ");
                for (String argument : remainder)
                {
                    System.out.print(argument);
                    System.out.print(" ");
                }

                System.out.println();
            }

        }
        catch (ParseException exception)
        {
            System.out.print("Parse error: ");
            System.out.println(exception.getMessage());
        }
    }

You could just do it manually.你可以手动完成。

NB: might be better to use a HashMap instead of an inner class for the opts.注意:对于 opts,使用 HashMap 而不是内部类可能会更好。

/** convenient "-flag opt" combination */
private class Option {
     String flag, opt;
     public Option(String flag, String opt) { this.flag = flag; this.opt = opt; }
}

static public void main(String[] args) {
    List<String> argsList = new ArrayList<String>();  
    List<Option> optsList = new ArrayList<Option>();
    List<String> doubleOptsList = new ArrayList<String>();

    for (int i = 0; i < args.length; i++) {
        switch (args[i].charAt(0)) {
        case '-':
            if (args[i].length < 2)
                throw new IllegalArgumentException("Not a valid argument: "+args[i]);
            if (args[i].charAt(1) == '-') {
                if (args[i].length < 3)
                    throw new IllegalArgumentException("Not a valid argument: "+args[i]);
                // --opt
                doubleOptsList.add(args[i].substring(2, args[i].length));
            } else {
                if (args.length-1 == i)
                    throw new IllegalArgumentException("Expected arg after: "+args[i]);
                // -opt
                optsList.add(new Option(args[i], args[i+1]));
                i++;
            }
            break;
        default:
            // arg
            argsList.add(args[i]);
            break;
        }
    }
    // etc
}

I like this one.我喜欢这一个。 Simple, and you can have more than one parameter for each argument:很简单,每个参数可以有多个参数:

final Map<String, List<String>> params = new HashMap<>();

List<String> options = null;
for (int i = 0; i < args.length; i++) {
    final String a = args[i];

    if (a.charAt(0) == '-') {
        if (a.length() < 2) {
            System.err.println("Error at argument " + a);
            return;
        }

        options = new ArrayList<>();
        params.put(a.substring(1), options);
    }
    else if (options != null) {
        options.add(a);
    }
    else {
        System.err.println("Illegal parameter usage");
        return;
    }
}

For example:例如:

-arg1 1 2 --arg2 3 4

System.out.print(params.get("arg1").get(0)); // 1
System.out.print(params.get("arg1").get(1)); // 2
System.out.print(params.get("-arg2").get(0)); // 3
System.out.print(params.get("-arg2").get(1)); // 4

Here is @DwB solution upgraded to Commons CLI 1.3.1 compliance (replaced deprecated components OptionBuilder and GnuParser).这是升级到 Commons CLI 1.3.1 合规性的 @DwB 解决方案(替换了已弃用的组件 OptionBuilder 和 GnuParser)。 The Apache documentation uses examples that in real life have unmarked/bare arguments but ignores them. Apache 文档使用了现实生活中具有未标记/裸参数但忽略它们的示例。 Thanks @DwB for showing how it works.感谢@DwB 展示它是如何工作的。

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;

public static void main(String[] parameters) {
    CommandLine commandLine;
    Option option_A = Option.builder("A").argName("opt3").hasArg().desc("The A option").build();
    Option option_r = Option.builder("r").argName("opt1").hasArg().desc("The r option").build();
    Option option_S = Option.builder("S").argName("opt2").hasArg().desc("The S option").build();
    Option option_test = Option.builder().longOpt("test").desc("The test option").build();
    Options options = new Options();
    CommandLineParser parser = new DefaultParser();

    options.addOption(option_A);
    options.addOption(option_r);
    options.addOption(option_S);
    options.addOption(option_test);

    String header = "               [<arg1> [<arg2> [<arg3> ...\n       Options, flags and arguments may be in any order";
    String footer = "This is DwB's solution brought to Commons CLI 1.3.1 compliance (deprecated methods replaced)";
    HelpFormatter formatter = new HelpFormatter();
    formatter.printHelp("CLIsample", header, options, footer, true);    

    String[] testArgs =
            { "-r", "opt1", "-S", "opt2", "arg1", "arg2",
                    "arg3", "arg4", "--test", "-A", "opt3", };

    try
    {
        commandLine = parser.parse(options, testArgs);

        if (commandLine.hasOption("A"))
        {
            System.out.print("Option A is present.  The value is: ");
            System.out.println(commandLine.getOptionValue("A"));
        }

        if (commandLine.hasOption("r"))
        {
            System.out.print("Option r is present.  The value is: ");
            System.out.println(commandLine.getOptionValue("r"));
        }

        if (commandLine.hasOption("S"))
        {
            System.out.print("Option S is present.  The value is: ");
            System.out.println(commandLine.getOptionValue("S"));
        }

        if (commandLine.hasOption("test"))
        {
            System.out.println("Option test is present.  This is a flag option.");
        }

        {
            String[] remainder = commandLine.getArgs();
            System.out.print("Remaining arguments: ");
            for (String argument : remainder)
            {
                System.out.print(argument);
                System.out.print(" ");
            }

            System.out.println();
        }

    }
    catch (ParseException exception)
    {
        System.out.print("Parse error: ");
        System.out.println(exception.getMessage());
    }

}

Output:输出:

usage: CLIsample [-A <opt3>] [-r <opt1>] [-S <opt2>] [--test]
                 [<arg1> [<arg2> [<arg3> ...
       Options, flags and arguments may be in any order
 -A <opt3>   The A option
 -r <opt1>   The r option
 -S <opt2>   The S option
    --test   The test option
This is DwB's solution brought to Commons CLI 1.3.1 compliance (deprecated
methods replaced)
Option A is present.  The value is: opt3
Option r is present.  The value is: opt1
Option S is present.  The value is: opt2
Option test is present.  This is a flag option.
Remaining arguments: arg1 arg2 arg3 arg4

I realize that the question mentions a preference for Commons CLI, but I guess that when this question was asked, there was not much choice in terms of Java command line parsing libraries.我意识到这个问题提到了对 Commons CLI 的偏好,但我想当被问到这个问题时,在 Java 命令行解析库方面没有太多选择。 But nine years later, in 2020, would you not rather write code like the below?但是九年后,也就是 2020 年,您是否宁愿编写如下代码?

import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;
import java.io.File;
import java.util.List;
import java.util.concurrent.Callable;

@Command(name = "myprogram", mixinStandardHelpOptions = true,
  description = "Does something useful.", version = "1.0")
public class MyProgram implements Callable<Integer> {

    @Option(names = "-r", description = "The r option") String rValue;
    @Option(names = "-S", description = "The S option") String sValue;
    @Option(names = "-A", description = "The A file") File aFile;
    @Option(names = "--test", description = "The test option") boolean test;
    @Parameters(description = "Positional params") List<String> positional;

    @Override
    public Integer call() {
        System.out.printf("-r=%s%n", rValue);
        System.out.printf("-S=%s%n", sValue);
        System.out.printf("-A=%s%n", aFile);
        System.out.printf("--test=%s%n", test);
        System.out.printf("positionals=%s%n", positional);
        return 0;
    }

    public static void main(String... args) {
        System.exit(new CommandLine(new MyProgram()).execute(args));
    }
}

Execute by running the command in the question:通过运行问题中的命令来执行:

java MyProgram -r opt1 -S opt2 arg1 arg2 arg3 arg4 --test -A opt3

What I like about this code is that it is:我喜欢这段代码的地方在于:

  • compact - no boilerplate紧凑 - 没有样板
  • declarative - using annotations instead of a builder API声明性 - 使用注释而不是构建器 API
  • strongly typed - annotated fields can be any type, not just String强类型 - 带注释的字段可以是任何类型,而不仅仅是字符串
  • no duplication - option declaration and getting parse result are together in the annotated field无重复 - 选项声明和获取解析结果一起在注释字段中
  • clear - the annotations express the intention better than imperative code clear - 注释比命令式代码更能表达意图
  • separation of concerns - the business logic in the call method is free of parsing-related logic关注点分离—— call方法中的业务逻辑没有解析相关的逻辑
  • convenient - one line of code in main wires up the parser and runs the business logic in the Callable方便 - main 中的一行代码连接解析器并在 Callable 中运行业务逻辑
  • powerful - automatic usage and version help with the built-in --help and --version options强大 - 使用内置的--help--version选项自动使用和版本帮助
  • user-friendly - usage help message uses colors to contrast important elements like option names from the rest of the usage help to reduce the cognitive load on the user用户友好 - 使用帮助消息使用颜色来对比重要元素(如选项名称)与其余使用帮助减少用户的认知负担

The above functionality is only part of what you get when you use the picocli ( https://picocli.info ) library.上述功能只是您使用 picocli ( https://picocli.info ) 库时获得的部分功能。

Now, bear in mind that I am totally, completely, and utterly biased, being the author of picocli.现在,请记住,作为 picocli 的作者,我完全、完全和完全有偏见。 :-) But I do believe that in 2020 we have better alternatives for building a command line apps than Commons CLI. :-) 但我确实相信,在 2020 年,我们有比 Commons CLI 更好的命令行应用程序替代方案。

You could use https://github.com/jankroken/commandline , here's how to do that:您可以使用https://github.com/jankroken/commandline ,这是如何做到的:

To make this example work, I must make assumptions about what the arguments means - just picking something here...为了使这个例子起作用,我必须对参数的含义做出假设——只是在这里选择一些东西......

-r opt1 => replyAddress=opt1
-S opt2 arg1 arg2 arg3 arg4 => subjects=[opt2,arg1,arg2,arg3,arg4]
--test = test=true (default false)
-A opt3 => address=opt3

this can then be set up this way:然后可以这样设置:

public class MyProgramOptions {
  private String replyAddress;
  private String address;
  private List<String> subjects;
  private boolean test = false;

  @ShortSwitch("r")
  @LongSwitch("replyAddress") // if you also want a long variant. This can be skipped
  @SingleArgument
  public void setReplyAddress(String replyAddress) {
    this.replyAddress = replyAddress;
  }

  @ShortSwitch("S")
  @AllAvailableArguments
  public void setSubjects(List<String> subjects) {
    this.subjects = subjects;
  }

  @LongSwitch("test")
  @Toggle(true)
  public void setTest(boolean test) {
    this.test = test;
  }

  @ShortSwitch("A")
  @SingleArgument
  public void setAddress(String address) {
    this.address = address;
  }

  // getters...
}

and then in the main method, you can just do:然后在 main 方法中,您可以执行以下操作:

public final static void main(String[] args) {
  try {
    MyProgramOptions options = CommandLineParser.parse(MyProgramOptions.class, args, OptionStyle.SIMPLE);

    // and then you can pass options to your application logic...

  } catch
    ...
  }
}

Simple code for command line in java: java中命令行的简单代码:

class CMDLineArgument
{
    public static void main(String args[])
    {
        String name=args[0];
        System.out.println(name);
    }
}

You could use the refcodes-console artifact at refcodes-console on REFCODES.ORG:您可以使用refcodes-console的神器refcodes控制台上REFCODES.ORG:

Option<String> r     = new StringOptionImpl( "-r", null, "opt1", "..." );
Option<String> s     = new StringOptionImpl( "-S", null, "opt2", "..." );
Operand<String> arg1 = new StringOperandImpl( "arg1", "..." );
Operand<String> arg2 = new StringOperandImpl( "arg2", "..." );
Operand<String> arg3 = new StringOperandImpl( "arg3", "..." );
Operand<String> arg4 = new StringOperandImpl( "arg4", "..." );
Switch test          = new SwitchImpl( null, "--test", "..." );
Option<String> a     = new StringOptionImpl( "-A", null, "opt3", "..." );
Condition theRoot    = new AndConditionImpl( r, s, a, arg1, arg2, arg3, arg4,
    test );

Create your arguments parser ArgsParserImpl with your root condition:使用您的根条件创建参数解析器ArgsParserImpl

ArgsParser theArgsParser = new ArgsParserImpl( theRoot );
theArgsParser.setName( "MyProgramm" );
theArgsParser.setSyntaxNotation( SyntaxNotation.GNU_POSIX );

Above you define your syntax, below you invoke the parser:上面定义语法,下面调用解析器:

theArgsParser.printUsage();
theArgsParser.printSeparatorLn();
theArgsParser.printOptions();
theArgsParser.evalArgs( new String[] {
    "-r", "RRRRR", "-S", "SSSSS", "11111", "22222", "33333", "44444", 
    "--test", "-A", "AAAAA"
} );

In case you provided some good descriptions, theArgsParser.printUsage() will even show you the pretty printed usage:如果您提供了一些很好的描述, theArgsParser.printUsage()甚至会向您展示漂亮的打印用法:

Usage: MyProgramm -r <opt1> -S <opt2> -A <opt3> arg1 arg2 arg3 arg4 --test

In the above example all defined arguments must be passed by the user, else the parser will detect a wrong usage.在上面的例子中,所有定义的参数都必须由用户传递,否则解析器将检测到错误的用法。 In case the --test switch is to be optional (or any other argument), assign theRoot as follows:如果--test开关是可选的(或任何其他参数),请按如下方式分配theRoot

theRoot = new AndConditionImpl( r, s, a, arg1, arg2, arg3, arg4, new OptionalImpl( test ) ); theRoot = new AndConditionImpl( r, s, a, arg1, arg2, arg3, arg4, new OptionalImpl( test ) );

Then your syntax looks as follows:然后您的语法如下所示:

Usage: MyProgramm -r <opt1> -S <opt2> -A <opt3> arg1 arg2 arg3 arg4 [--test]

The full example for your case you find in the StackOverFlowExamle .您可以在StackOverFlowExamle 中找到您案例的完整示例。 You can use AND, OR, XOR conditions and any kind of nesting ... hope this helps.您可以使用 AND、OR、XOR 条件和任何类型的嵌套......希望这会有所帮助。

Evaluate the parsed arguments as follows: r.getValue() );评估解析的参数如下: r.getValue() ); or if (test.getValue() == true) ... :或者if (test.getValue() == true) ...

LOGGER.info( "r    :=" + r.getValue() );
LOGGER.info( "S    :=" + s.getValue() );
LOGGER.info( "arg1 :=" + arg1.getValue() );
LOGGER.info( "arg2 :=" + arg2.getValue() );
LOGGER.info( "arg3 :=" + arg3.getValue() );
LOGGER.info( "arg4 :=" + arg4.getValue() );
LOGGER.info( "test :=" + test.getValue() + "" );
LOGGER.info( "A    :=" + a.getValue() );

Ok, thanks to Charles Goodwin for the concept.好的,感谢 Charles Goodwin 的概念。 Here is the answer:这是答案:

import java.util.*;
public class Test {

  public static void main(String[] args) {
     List<String> argsList  = new ArrayList<String>();  
     List<String> optsList  = new ArrayList<String>();
     List<String> doubleOptsList  = new ArrayList<String>();
     for (int i=0; i < args.length; i++) {
         switch (args[i].charAt(0)) {
         case '-':
             if (args[i].charAt(1) == '-') {
                 int len = 0;
                 String argstring = args[i].toString();
                 len = argstring.length();
                 System.out.println("Found double dash with command " +
                     argstring.substring(2, len) );
                 doubleOptsList.add(argstring.substring(2, len));           
             } else {
                 System.out.println("Found dash with command " + 
                   args[i].charAt(1) + " and value " + args[i+1] );   
                 i= i+1;
                 optsList.add(args[i]);      
             }           
         break;         
         default:            
         System.out.println("Add a default arg." );
         argsList.add(args[i]);
         break;         
         }     
     } 
  } 

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM