添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

昨天已经联系过几个基本的命令行参数使用场景, 可以参考这里

通过使用Apache Commons CLI可以帮助开发者快速构建命令行启动模式,并可以快速生成帮助指令,基于用户启动参数提供不同的服务。

下面就举个例子,比如我想输入命令:

xxx -t

从而打印出当前的时间。

定义阶段——创建选项

首先要创建Options对象,然后添加Option对象.

// create Options object
Options options = new Options();
// add t option
options.addOption("t", false, "display current time");

其中addOption方法有三个参数:

  • 第一个参数是字符串类型,代表命令的参数。
  • 第二个参数是Bool型,代表该选项是否需要额外的参数。
  • 第三个参数是该选项的描述信息。
  • 上面的例子就代表,t选项不需要参数,它的意思是显示当前时间

    解析阶段——解析命令行参数

    CLI通过CommandLineParser的parse方法解析命令行参数。有好几种CommandLineParser的实现类,推荐使用的是DefaultParser。看源码除了这个DefaultParser,其他的都被打上了@Deprecated标记。
    除去这个DefaultParser以外,还有一个抽象类实现了CommandLineParser接口——Parser,这个Parser有三个子类:

  • BasicParser
  • GnuParser
  • PosixParser
  • CommandLineParser parser = new DefaultParser();
    CommandLine cmd = parser.parse( options, args);
    

    询问阶段—— 判断命令行中出现了哪个选项

    现在就可以检查是否存在这个t选项了,首先需要在CommandLine对象中进行查询。hasOption方法可以通过选项的名字,判断命令行是否出现该命令。出现则返回true,否则返回false。

    if(cmd.hasOption("t")) {
        // print the date and time
    else {
        // print the date
    
    public class DateApp {
    	public static void main(String[] args) {
    		String[] arg = {"-t"};
    //		String[] arg = {};
    		try {
    			testOption(arg);
    		} catch (ParseException e) {
    			e.printStackTrace();
    	static void testOption(String[] args) throws ParseException{
    		Options options = new Options();
    		options.addOption("t", false, "display current time");
    		CommandLineParser parser = new DefaultParser();
    		CommandLine cmd = parser.parse( options, args);
    		if(cmd.hasOption("t")) {
    			System.out.println((new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).format(new Date()));
    		}else {
    			System.out.println((new SimpleDateFormat("yyyy-MM-dd")).format(new Date()));
    

    下面这个例子继承自DateApp,并且提供了根据城市显示时间和日期的功能。为了实现这个命令,会增加一个c选项,来添加城市信息。

    // add c option
    options.addOption("c", true, "country code");
    

    第二个参数这时设置为true,就代表它需要额外的参数。

    获得参数值

    CommandLine对象的getOptionValue方法可以获取到参数的值。

    // get c option value
    String countryCode = cmd.getOptionValue("c");
    if(countryCode == null) {
        // print default date
    }else {
        // print date for country specified by countryCode
    

    此时如果只有c选项,没有参数,就会报错

    org.apache.commons.cli.MissingArgumentException: Missing argument for option: c
    	at org.apache.commons.cli.DefaultParser.checkRequiredArgs(DefaultParser.java:211)
    	at org.apache.commons.cli.DefaultParser.parse(DefaultParser.java:125)
    	at org.apache.commons.cli.DefaultParser.parse(DefaultParser.java:76)
    	at org.apache.commons.cli.DefaultParser.parse(DefaultParser.java:60)
    	at hangout.study.InternationalDateApp.testOption(InternationalDateApp.java:29)
    	at hangout.study.InternationalDateApp.main(InternationalDateApp.java:18)
    
    public class InternationalDateApp {
    	public static void main(String[] args) {
    		String[] arg = {"-t","-c","hello"};
    //		String[] arg = {"-t","-c"};
    //		String[] arg = {};
    		try {
    			testOption(arg);
    		} catch (ParseException e) {
    			e.printStackTrace();
    	static void testOption(String[] args) throws ParseException{
    		Options options = new Options();
    		options.addOption("t", false, "display current time");
    		options.addOption("c", true, "country code");
    		CommandLineParser parser = new DefaultParser();
    		CommandLine cmd = parser.parse( options, args);
    		if(cmd.hasOption("t")) {
    			System.out.println((new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).format(new Date())+" in "+cmd.getOptionValue("c"));
    		}else {
    			System.out.println((new SimpleDateFormat("yyyy-MM-dd")).format(new Date()));
    

    此时运行代码,则会正确输出信息:

    2016-06-23 21:18:50 in hello
    

    Ant样例

    下面举一个Ant的样例,下面是Ant输出的帮助信息

    ant [options] [target [target2 [target3] ...]]
      Options: 
      -help                  print this message
      -projecthelp           print project help information
      -version               print the version information and exit
      -quiet                 be extra quiet
      -verbose               be extra verbose
      -debug                 print debugging information
      -emacs                 produce logging information without adornments
      -logfile <file>        use given file for log
      -logger <classname>    the class which is to perform logging
      -listener <classname>  add an instance of class as a project listener
      -buildfile <file>      use given buildfile
      -D<property>=<value>   use value for given property
      -find <file>           search for buildfile towards the root of the
                             filesystem and use it
    

    使用Bool选项创建

    Option help = new Option( "help", "print this message" );
    Option projecthelp = new Option( "projecthelp", "print project help information" );
    Option version = new Option( "version", "print the version information and exit" );
    Option quiet = new Option( "quiet", "be extra quiet" );
    Option verbose = new Option( "verbose", "be extra verbose" );
    Option debug = new Option( "debug", "print debugging information" );
    Option emacs = new Option( "emacs","produce logging information without adornments" );
    

    也可以使用OptionBuilder构建选项:

    Option logfile   = OptionBuilder.withArgName( "file" )
           .hasArg()
           .withDescription(  "use given file for log" )
           .create( "logfile" );
    Option logger    = OptionBuilder.withArgName( "classname" )
           .hasArg()
           .withDescription( "the class which it to perform "+ "logging" )
           .create( "logger" );
    Option listener  = OptionBuilder.withArgName( "classname" )
           .hasArg()
           .withDescription( "add an instance of class as "+ "a project listener" )
           .create( "listener"); 
    Option buildfile = OptionBuilder.withArgName( "file" )
           .hasArg()
           .withDescription(  "use given buildfile" )
           .create( "buildfile");
    Option find      = OptionBuilder.withArgName( "file" )
           .hasArg()
           .withDescription( "search for buildfile towards the "+ "root of the filesystem and use it" )
           .create( "find" );
    

    最后一个OptionBuilder创建带有参数名称的选项:

    Option property  = OptionBuilder.withArgName( "property=value" )
                                    .hasArgs(2)
                                    .withValueSeparator()
                                    .withDescription( "use value for given property" )
                                    .create( "D" );
    

    通过上面的方式定义的属性,可以通过CommandLine对象的getOptionProperties("D")方法获得。

    定义阶段——创建选项

    Options options = new Options();
    options.addOption( help );
    options.addOption( projecthelp );
    options.addOption( version );
    options.addOption( quiet );
    options.addOption( verbose );
    options.addOption( debug );
    options.addOption( emacs );
    options.addOption( logfile );
    options.addOption( logger );
    options.addOption( listener );
    options.addOption( buildfile );
    options.addOption( find );
    options.addOption( property );
    

    解析阶段——创建解析器

    跟前面类似,创建CommandLineParser解析器,返回CommandLine对象,用于查询选项参数。

    public static void main( String[] args ) {
        // create the parser
        CommandLineParser parser = new DefaultParser();
        try {
            // parse the command line arguments
            CommandLine line = parser.parse( options, args );
        catch( ParseException exp ) {
            // oops, something went wrong
            System.err.println( "Parsing failed.  Reason: " + exp.getMessage() );
    

    询问阶段——查询命令行参数

    通过hasOption选项判断是否包含某个选项参数:

    // has the buildfile argument been passed?
    if( line.hasOption( "buildfile" ) ) {
        // initialise the member variable
        this.buildfile = line.getOptionValue( "buildfile" );
    

    创建帮助信息

    一般命令行工具都有help帮助提示,即输入-h命令,就会输出所有的命令参数。CLI提供给我们快捷输出帮助信息的工具——HelpFormatter。

    // automatically generate the help statement
    HelpFormatter formatter = new HelpFormatter();
    formatter.printHelp( "ant", options );
    

    当执行到此处时,就会输出相应的帮助信息

    usage: ant
    -D <property=value>     use value for given property
    -buildfile <file>       use given buildfile
    -debug                  print debugging information
    -emacs                  produce logging information without adornments
    -file <file>            search for buildfile towards the root of the
                            filesystem and use it
    -help                   print this message
    -listener <classname>   add an instance of class as a project listener
    -logger <classname>     the class which it to perform logging
    -projecthelp            print project help information
    -quiet                  be extra quiet
    -verbose                be extra verbose
    -version                print the version information and exit
    
    public class AntExample {
    	public static void main(String[] args) {
    		String[] arg = {"-help"};
    		testOption(arg);
    	@SuppressWarnings({ "deprecation", "static-access" })
    	static void testOption(String[] args){
    		Option help = new Option( "help", "print this message" );
    		Option projecthelp = new Option( "projecthelp", "print project help information" );
    		Option version = new Option( "version", "print the version information and exit" );
    		Option quiet = new Option( "quiet", "be extra quiet" );
    		Option verbose = new Option( "verbose", "be extra verbose" );
    		Option debug = new Option( "debug", "print debugging information" );
    		Option emacs = new Option( "emacs","produce logging information without adornments" );
    		Option logfile   = OptionBuilder.withArgName( "file" )
                    .hasArg()
                    .withDescription(  "use given file for log" )
                    .create( "logfile" );
    		Option logger    = OptionBuilder.withArgName( "classname" )
                    .hasArg()
                    .withDescription( "the class which it to perform "+ "logging" )
                    .create( "logger" );
    		Option listener  = OptionBuilder.withArgName( "classname" )
                    .hasArg()
                    .withDescription( "add an instance of class as "+ "a project listener" )
                    .create( "listener"); 
    		Option buildfile = OptionBuilder.withArgName( "file" )
                    .hasArg()
                    .withDescription(  "use given buildfile" )
                    .create( "buildfile");
    		Option find = OptionBuilder.withArgName( "file" )
                    .hasArg()
                    .withDescription( "search for buildfile towards the " + "root of the filesystem and use it" )
                    .create( "find" );
    		Option property  = OptionBuilder.withArgName( "property=value" )
                    .hasArgs(2)
                    .withValueSeparator()
                    .withDescription( "use value for given property" )
                    .create( "D" );
    		Options options = new Options();
    		options.addOption( help );
    		options.addOption( projecthelp );
    		options.addOption( version );
    		options.addOption( quiet );
    		options.addOption( verbose );
    		options.addOption( debug );
    		options.addOption( emacs );
    		options.addOption( logfile );
    		options.addOption( logger );
    		options.addOption( listener );
    		options.addOption( buildfile );
    		options.addOption( find );
    		options.addOption( property );
    	    CommandLineParser parser = new DefaultParser();
    	    try {
    	        CommandLine line = parser.parse( options, args );
    		    if( line.hasOption( "buildfile" ) ) {
    		       System.out.println(line.getOptionValue( "buildfile" ));
    		    if( line.hasOption("help")){
    			    HelpFormatter formatter = new HelpFormatter();
    			    formatter.printHelp( "ant", options );
    	    }catch( ParseException exp ) {
    	        System.err.println( "Parsing failed.  Reason: " + exp.getMessage() );
    

    运行后得到下面的输出信息:

    usage: ant
     -buildfile <file>       use given buildfile
     -D <property=value>     use value for given property
     -debug                  print debugging information
     -emacs                  produce logging information without adornments
     -find <file>            search for buildfile towards the root of the
                             filesystem and use it
     -help                   print this message
     -listener <classname>   add an instance of class as a project listener
     -logfile <file>         use given file for log
     -logger <classname>     the class which it to perform logging
     -projecthelp            print project help information
     -quiet                  be extra quiet
     -verbose                be extra verbose
     -version                print the version information and exit
    

    如果想在输出信息中加入参数列表,也可以在printHelp加入第三个参数true, formatter.printHelp( "ant", options, true);

    usage: ant [-buildfile <file>] [-D <property=value>] [-debug] [-emacs]
           [-find <file>] [-help] [-listener <classname>] [-logfile <file>]
           [-logger <classname>] [-projecthelp] [-quiet] [-verbose] [-version]
     -buildfile <file>       use given buildfile
     -D <property=value>     use value for given property
     -debug                  print debugging information
     -emacs                  produce logging information without adornments
     -find <file>            search for buildfile towards the root of the
                             filesystem and use it
     -help                   print this message
     -listener <classname>   add an instance of class as a project listener
     -logfile <file>         use given file for log
     -logger <classname>     the class which it to perform logging
     -projecthelp            print project help information
     -quiet                  be extra quiet
     -verbose                be extra verbose
     -version                print the version information and exit
    

    这个例子模拟了Linux下的命令行使用帮助:

    全部代码:

    public class LSExample {
    	public static void main(String[] args) {
    		String[] arg = new String[]{ "--block-size=10" };
    		testOption(arg);
    	static void testOption(String[] args){
    		CommandLineParser parser = new DefaultParser();
    		// create the Options
    		Options options = new Options();
    		options.addOption( "a", "all", false, "do not hide entries starting with ." );
    		options.addOption( "A", "almost-all", false, "do not list implied . and .." );
    		options.addOption( "b", "escape", false, "print octal escapes for nongraphic "
    		                                         + "characters" );
    		options.addOption( OptionBuilder.withLongOpt( "block-size" )
    		                                .withDescription( "use SIZE-byte blocks" )
    		                                .hasArg()
    		                                .withArgName("SIZE")
    		                                .create() );
    		options.addOption( "B", "ignore-backups", false, "do not list implied entried "
    		                                                 + "ending with ~");
    		options.addOption( "c", false, "with -lt: sort by, and show, ctime (time of last " 
    		                               + "modification of file status information) with "
    		                               + "-l:show ctime and sort by name otherwise: sort "
    		                               + "by ctime" );
    		options.addOption( "C", false, "list entries by columns" );
    		try {
    		    CommandLine line = parser.parse( options, args );
    		    if( line.hasOption( "block-size" ) ) {
    		        System.out.println( line.getOptionValue( "block-size" ) );
    		catch( ParseException exp ) {
    		    System.out.println( "Unexpected exception:" + exp.getMessage() );
    

    输出可以得到下面的信息:

    usage: ls -a,--all do not hide entries starting with . -A,--almost-all do not list implied . and .. -b,--escape print octal escapes for nongraphic characters -B,--ignore-backups do not list implied entried ending with ~ --block-size <SIZE> use SIZE-byte blocks -c with -lt: sort by, and show, ctime (time of last modification of file status information) with -l:show ctime and sort by name otherwise: sort by ctime -C list entries by columns

    我的前一天总结
    Apache Common CLI官方文档